Skip to content
txbrendan/ui

Search Field

beta

A search box with the same sizes as Input. A clear button shows once there is text, Esc clears it, and an optional count says how many matched.

Preview

count

size

states

3 matches
No matches

Install

  1. 1. Add the registry to components.json, once per app. Without it, the shadcn CLI looks up @txbrendan in a public list that txbrendan/ui does not control.

    npx shadcn@latest registry add "@txbrendan=https://txbrendan.dev/r/{name}.json"
  2. 2. Add the theme, once per app. It replaces your color tokens with ones checked for contrast.

    npx shadcn@latest add @txbrendan/theme
  3. 3. Add the component.

    npx shadcn@latest add @txbrendan/search-field

Use it for

  • Filtering a list or a table on the same page as people type.
  • Give it a name: aria-label, like "Search people", or an id that a visible label's htmlFor points at.
  • Pass count with the number of matches. It shows in the field, like "3 people" or "No people", and is announced politely. Set noun to the singular and plural words.
  • Pass shortcut to focus the field from anywhere on the page: one key, like "/", or "Mod+K" for ⌘K on a Mac and Ctrl+K elsewhere. It shows as a quiet key at the end of the empty field, and hides once people type, on touch screens, and while disabled.
  • Esc clears the text. With the field empty, Esc leaves it, and goes on to any dialog around it.
  • Use onEnter to run a search that waits for Enter, like one that asks a server.

Avoid it for

  • Giving two parts on one page the same shortcut. Command Menu and Rail's onSearch listen for ⌘K too, so pass "Mod+K" only where neither is on the page.
  • Picking one value from a list. Use a combobox.
  • Using the placeholder as the name. It disappears as soon as people type.
  • A search that opens a results page. A plain input in a form with a submit button works better there.

Props

  • value

    The typed text. Use it with onValueChange.

    string

  • defaultValue

    The text at first render, when you do not control it.

    string

  • onValueChange

    Called with the new text on every change, and with "" when cleared.

    (value: string) => void

  • onEnter

    Called with the text when Enter is pressed.

    (value: string) => void

  • size

    Height, padding, and text size. The same heights as Input.

    "sm" | "md" | "lg" · default "md"

  • count

    How many matched. Shows in the field and is announced. Hidden while the field is empty.

    number | null

  • noun

    The singular and plural words for the count.

    [string, string] · default ["match", "matches"]

  • shortcut

    Focuses the field from anywhere. One key, like "/", is ignored while people type in another field. "Mod+" and one key, like "Mod+K", is ⌘K on a Mac and Ctrl+K elsewhere, and works from another field too, but not in a rich text editor. Shows as a ghost Kbd while the field is empty, with a letter in capitals, as the keyboard prints it.

    string

  • disabled

    Blocks typing and hides the clear button.

    boolean · default false

  • placeholder

    A hint inside the empty field, like "Search people".

    string

  • aria-label

    Names the field. Required unless you pass aria-labelledby, or an id with a visible label.

    string

  • className

    Classes for the field's box. Other props, like name and id, land on the input.

    string

Accessibility

  • A native input with type="search", so screen readers call it a search field.
  • Its type needs a name: aria-label, aria-labelledby, or an id for a label's htmlFor.
  • The count sits in a polite live region, so a new count is read out without moving focus.
  • Esc clears the text first, and marks the field data-owns-escape while it has text. A dialog around it closes only on the next Esc.
  • The clear button has the name "Clear search". It is not a Tab stop, since Esc does the same from the keyboard. It is at least 24px square, and 44px on a coarse pointer.
  • shortcut sets aria-keyshortcuts, so screen readers can say the key. "Mod+K" sets "Meta+K Control+K". The printed key is aria-hidden, so it is not read twice.
  • On touch screens the text is 16px, so iOS Safari does not zoom in when the field gets focus.

Examples

Jump in with ⌘K

<SearchField aria-label="Search the app" placeholder="Search" shortcut="Mod+K" />

Filter a list

const people = ["Dan Kerr", "Maya Patel", "Tom Hale"];
const [query, setQuery] = useState("");
const shown = people.filter((name) => name.toLowerCase().includes(query.toLowerCase()));

<SearchField
  aria-label="Search people"
  placeholder="Search people"
  value={query}
  onValueChange={setQuery}
  count={shown.length}
  noun={["person", "people"]}
  shortcut="/"
/>

Small, in a toolbar

<SearchField size="sm" aria-label="Search matches" placeholder="Search" />