Typeset

Long-form content, such as Markdown or chat replies, set for reading.

Building a Live Search Box

The useSearch hook turns a text field into instant search. It debounces typing, cancels stale requests, and keeps results in step with the query so the list never flashes old matches.

In short, useSearch gives you:

  • Debounced queries: requests wait until typing pauses, so the server sees one call instead of ten.
  • Cancellation: a new query aborts the previous request before its results arrive.
  • Result state: loading, empty, and error states come back with the matches.

This guide builds a search box for a project list. For keyboard navigation through results, see the command menu guide.

Example

Each keystroke follows the same path:

  1. The field updates query immediately so typing never lags.
  2. After 200ms without typing, the hook sends the request.
  3. Results replace the list only if they belong to the latest query.
Search should feel like filtering, not waiting. Show what you have immediately and refine it as better results arrive.

Customizing results

Pass transform to reshape each result before it renders, for example to highlight the matched text or group projects by team.

Status

The status value is one of:

  • idle: the field is empty and no request has been made.
  • loading: a request is in flight; earlier results stay visible.
  • ready: results match the current query.
  • error: the last request failed; retry keeps the current query.

Options

OptionDefaultDescription
debounce200Milliseconds to wait after typing stops.
minLength1Characters required before searching.
limit20Maximum results per request.

Results are cached per query for the life of the component, so clearing the field and typing the same words again is instant.

Fig. 1 A rendered article: headings, paragraphs, lists, and code, set with the shared type roles.

When to use it

Wrap rendered Markdown, help articles, and chat replies in Typeset. It applies the shared type roles with reading rhythm, so you don’t style each element.

Keep app interface out of it: settings rows, menus, and controls use their own denser roles.

How it behaves

Wrap rendered content in Typeset; it styles headings, paragraphs, lists, links, inline code, code blocks, quotes, tables, rules, and images inside it.

Headings reuse heading-24, heading-20, and heading-16; text uses body-md in the lighter --foreground-body tone. App UI keeps its denser roles outside Typeset.

Spacing is relative to the body size. Lists sit close to the sentence that introduces them, and blocks get a little more room than paragraphs.

Set --typeset-flow, --typeset-block, or --typeset-tight through className to tighten or loosen the rhythm, for example in a chat reply.

Plain pre blocks from Markdown scroll horizontally without bouncing. Render fenced code with CodeBlock for highlighting; Typeset spaces it like any other block. Links keep a faint underline so they are not identified by color alone.

Build it

Add it with the shadcn CLI:

Terminal

A complete example. Change the values to fit your product:

typeset-example.tsx

The full source is yours to edit once installed:

typeset.tsx