Expanding search
An icon that morphs into a search field with results beneath it.
pnpm dlx shadcn@latest add @uiarc/expanding-searchLive · keyboard ready
- Header or toolbar search where a full field would crowd the layout.
- Quick navigation across local items with grouped results and recent suggestions.
- Use search-field to filter a visible list in place.
- Use combobox to set a form value.
- Use command-palette for searching commands across the app.
Installation
Add Expanding search with the shadcn CLI, or copy the source by hand.
pnpm dlx shadcn@latest add @uiarc/expanding-searchAdds the component and its local dependencies, and installs motion, lucide-react. First time? Add the @uiarc registry to components.json, or use the full URL:
example.tsx
import { ExpandingSearch } from "@/components/arc/expanding-search/expanding-search"; export function HeaderSearch() { const router = useRouter(); return ( <ExpandingSearch label="Search projects and docs" items={[ { id: "p1", title: "Arc website", group: "Projects", meta: "Updated today" }, { id: "d1", title: "Motion tokens", group: "Docs", keywords: ["spring"] }, ]} onSelect={(item) => router.push(`/items/${item.id}`)} /> );}ExpandingSearch
An icon button that morphs into a search field with grouped, highlighted results beneath it.
PropTypeDefaultDescription
labelRequiredstring–Names the button, field, and results, such as "Search projects and docs".itemsRequiredExpandingSearchItem[]–{ id, title, meta?, group?, icon?, keywords? }. Filtered client-side by title and keywords.suggestionsExpandingSearchItem[][]Shown before anything is typed, such as recent searches.suggestionsLabelstring"Recent"Heading for suggestions.placeholderstring–Field placeholder. Defaults to label.onSelect(item: ExpandingSearchItem) => void–Called when a result is chosen; the field then folds back.onExpandedChange(expanded: boolean) => void–Called when the field opens or folds.expandedWidthnumber360Widest the field grows, never past its container.maxResultsnumber6Most results shown.anchor"start" | "end""end"Edge the button sits on; the field grows away from it.emptyHintstring"Try a shorter word or check the spelling."Tip under the empty state.classNamestring–Added to the root.- EnterorSpace
- On the icon button, expands the field.
- ArrowDownorArrowUp
- Moves through results, wrapping.
- Enter
- Chooses the active result.
- Escape
- Folds the field back into the icon.
- The field is role="combobox" with aria-expanded, aria-controls, aria-autocomplete="list", and aria-activedescendant.
- Results are a role="listbox" of role="option" items inside labelled role="group" sections.
- Result counts and empty states are announced through a polite live region; the collapsed field is inert.
- The button morphs into the field on a spring, results unfold beneath with a panel that tracks the field width, and a highlight travels between options.
- Reduced motion swaps states with fades and no width or position travel.
- The field grows to the smaller of expandedWidth and its container, and a ResizeObserver follows container resizes.
- The results panel matches the field width and caps at min(22rem, 60vh), scrolling itself.
- The idle lane takes no pointer events, so it never blocks what sits under it on small screens.
- Ranking is memoized and runs over all items per query; pass pre-filtered or server results for large sets.
- Result rows use layout position animation, capped by maxResults at 6 by default.
Notes for AI
Give your coding assistant the Markdown reference instead of screenshots.
- Use in headers and toolbars where a full field would crowd the layout. Use search-field to filter a list in place and combobox to set a form value.
- Place it in the space it may grow into; it fills that space up to expandedWidth. Filtering is local, so pass pre-fetched items or update items as the query changes.
- Exported as both named and default.
The full library index for assistants is at /llms.txt.