Search dialog — dir/ui
Search caller-provided results in a dialog with keyboard navigation and custom content.
# Search dialog
Search caller-provided results in a dialog with keyboard navigation and custom content.
## Install
```sh
npx shadcn@4.21.0 add lr-run/dir-ui/search-dialog#v0.1.2
```
[Registry item with source and dependencies](https://github.com/lr-run/dir-ui/blob/v0.1.2/registry.json)
Requires React 19, Tailwind CSS 4 and shadcn initialized with Base UI. Registry target placeholders resolve through components.json: @ui, @components, @hooks and @lib. Imports below use the conventional @/ prefix for illustration; the CLI rewrites them to your configured aliases. Shared components are installed once through registryDependencies. Theme CSS is imported by the UI modules. Existing files are never forcibly overwritten; review CLI conflicts before accepting changes. No Dir runtime is required.
## SearchDialog, SearchDialogTrigger, type SearchDialogProps, type SearchItem, type LoadSearchResults
```tsx
import { SearchDialog, SearchDialogTrigger, type SearchDialogProps, type SearchItem, type LoadSearchResults } from '@/components/collections/search-dialog.tsx'
```
| Prop | Type | Default / required | Notes |
| --- | --- | --- | --- |
| loadResults | LoadSearchResults | — | Remote adapter. Pass AbortSignal to fetch; return result items. Server order within each group is preserved without local filtering. |
| debounceMs | number | 250 | Remote search delay; cancels pending and in-flight requests on new input or close. |
| minQueryLength | number | 0 | Do not request results below this trimmed query length. |
| SearchDialogTrigger.placeholder / shortcut | string / boolean | Search… / false | Input-style button label and platform keyboard hint. Enable SearchDialog.shortcut separately. |
| SearchDialogTrigger.…buttonProps | ComponentProps<"button"> | — | Supply onClick, aria-expanded, disabled, className, and ref. |
| items | readonly SearchItem[] | [] | Local results or initial suggestions; ignored when loadResults is supplied. |
| onSelect | (item: SearchItem) => void \| Promise<void> | Required | Rejected promises display an error; duplicate selection is prevented. |
| query / onQueryChange | string / (query: string) => void | Uncontrolled | |
| filter | boolean | true | Applies to local items only. loadResults automatically bypasses local filtering. |
| loading / error / onRetry | boolean / string / () => void | — | |
| maxVisible | number | 100 | |
| placeholder | string | Search… | |
| inputLabel | string | Search | Accessible search field name. |
| emptyQueryLabel | string | Suggestions | Fallback group label for caller-provided initial results; no history is stored. |
| selectLabel | string | Select | Label of the footer selection button. |
| renderPreview | (item: SearchItem) => ReactNode | — | Optional content for the highlighted result. Updates on keyboard/pointer navigation; hidden below 600px. showPreview is a documentation-only control. |
| emptyMessage | ReactNode | No results found. | |
| renderItem | (item: SearchItem) => ReactNode | — | Customize result content; keyboard and selection behavior remain managed. |
| open / onOpenChange | boolean / (open: boolean) => void | Required | |
| title | string | Search | |
| shortcut | boolean | false | Opt in to one Cmd/Ctrl+K handler per application. |
```ts
type LoadSearchResults = (query: string, context: { signal: AbortSignal }) => Promise<readonly SearchItem[]>
type SearchItem = { id: string; label: string; group?: string; description?: string; keywords?: string[]; icon?: ReactNode; meta?: string; disabled?: boolean }
```