Search Bar
FreeCommand-palette style search with suggestions, filters and loading states.
import { SvSearchBarComponent } from '@styloviz/search-bar';Demo & usage
Basic usage
Usage example — open the gallery for the full interactive demo.
An interactive demo for this component lives in the gallery.
Open live demoMore examples
With filters & shortcut hint
<sv-search-bar placeholder="Search…" shortcutHint="⌘K" [filters]="filters" [loading]="isSearching" (searched)="run($event)" />
Grouped suggestions (Pro)
`groupByCategory` sections the dropdown by each suggestion's `category`, with a header before each contiguous run — pre-sort suggestions so same-category items are adjacent.
<sv-search-bar-pro [suggestions]="suggestions" [groupByCategory]="true" (searched)="run($event)" />
When to use
- Providing prominent search over a dataset with live suggestions.
- You want scoped filters and a keyboard-shortcut affordance in the header.
- For a full command/action launcher, use the Command Palette instead.
Async states
- loading
- empty
- error
- retry
Inputs
| Name | Type | Default | Description |
|---|---|---|---|
| error | SvDataError | string | null | null | Load failure. Use `toSvDataError()` from `@styloviz/core` to normalize an `HttpErrorResponse`. |
| value | string (two-way) | '' | Current input text. Use `[(value)]` for two-way binding. |
| label | string | '' | Floating label text — only shown when `variant="floating"`. Acts as a Material Design-style animated label. |
| placeholder | string | 'Search…' | Placeholder text. |
| ariaLabel | string | '' | Explicit accessible name for the input. Falls back to `label` then `placeholder`. |
| shortcutHint | string | '' | Keyboard shortcut hint shown on the right side of the input when empty. E.g. `'⌘K'`, `'Ctrl+K'`, `'/''`. Empty string hides the badge. |
| suggestions | SearchSuggestion[] | [] | Suggestion items to display in the dropdown. Pass a filtered/sliced array; the component renders all provided items. Empty array closes the dropdown. |
| suggestionsLabel | string | '' | Label shown above the suggestions list (e.g. 'Recent searches', 'Results'). Empty string hides the header. |
| footerLabel | string | '' | Text displayed at the bottom of the dropdown — useful for "View all results". Empty string hides the footer. |
| filters | SearchFilter[] | [] | Active filter chips rendered inside the input track. Each chip has an × button that emits `filterRemoved`. |
| size | SearchBarSize | 'md' | Field size: sm, md or lg. |
| variant | SearchBarVariant | 'default' | Visual variant: default, floating, minimal… |
| showSearchIcon | boolean | true | Show the magnifier icon on the left. |
| loading | boolean | false | Show the spinning loader inside the search icon area. |
| stale | boolean | false | The results on screen answer an **older** query — a newer request is still in flight. Distinct from `loading`, which cannot separate the two cases it covers: the first fetch (nothing on screen — show a skeleton) and a refinement of results already visible (keep them, and say they are provisional). Blanking the panel on the second is the most jarring thing a picker can do; showing the old list with no indication is a quiet lie. Bind `svSuggestController(...).stale` from `@styloviz/data`, or `createRequestGuard().stale` from `@styloviz/core`. The results dim and report `aria-busy` while staying fully interactive. |
| emptyReason | SvEmptyReason | null | null | Why the suggestion list is empty, or `null` when it is not empty (or when nothing has been asked yet and the panel should stay shut). Bind `svSuggestController(...).emptyReason` from `@styloviz/data`, which already distinguishes these two and holds `null` while a first load is in flight — an "opening" message that flashes over a search in progress is a third wrong answer. Leaving this unbound keeps the pre-existing behaviour exactly: an empty `suggestions` array closes the panel and says nothing. |
| noDataLabel | string | 'Type to search' | Copy for the opening state — nothing searched for yet. |
| noResultsLabel | string | 'No results found' | Copy for a search that answered with nothing. |
| noResultsHint | string | '' | A second line under `noResultsLabel` — "Check your spelling or try a broader term". Empty hides it. |
| disabled | boolean | false | Whether the input is disabled. |
| debounceMs | number | 300 | Debounce delay in milliseconds before `searched` emits. Set to 0 to emit on every keystroke. |
| customClass | string | '' | Additional classes on the root wrapper. |
Outputs
| Event | Payload type | Description |
|---|---|---|
| retry | void | The host should re-run the failed suggestion lookup. |
| searched | string | Emitted (debounced) on every value change. |
| submitted | string | Emitted when the user presses Enter or clicks a suggestion. |
| suggestionSelected | SearchSuggestion | Emitted when a suggestion row is clicked, carrying the suggestion object. |
| filterRemoved | string | Emitted when the × on a filter chip is clicked. |
| footerClick | void | Emitted when the footer link is clicked. |
| cleared | void | Emitted when the input is cleared via the × button. |
| valueChange | string | Emits the new `value` whenever it changes — the output half of `[(value)]`. |
<sv-search-bar-pro>
Also in this family — SvSearchBarProComponent from @styloviz-pro/search-bar.
Async states
- loading
- empty
- error
- retry
Inputs
| Name | Type | Default | Description |
|---|---|---|---|
| error | SvDataError | string | null | null | Load failure. Use `toSvDataError()` from `@styloviz/core` to normalize an `HttpErrorResponse`. |
| value | string (two-way) | '' | Current input text. Use `[(value)]` for two-way binding. |
| label | string | '' | Floating label text — only shown when `variant="floating"`. Acts as a Material Design-style animated label. |
| placeholder | string | 'Search…' | Placeholder text. |
| ariaLabel | string | '' | Explicit accessible name for the input. Falls back to `label` then `placeholder`. |
| shortcutHint | string | '' | Keyboard shortcut hint shown on the right side of the input when empty. E.g. `'⌘K'`, `'Ctrl+K'`, `'/''`. Empty string hides the badge. |
| suggestions | SearchSuggestion[] | [] | Suggestion items to display in the dropdown. Pass a filtered/sliced array; the component renders all provided items. Empty array closes the dropdown. |
| suggestionsLabel | string | '' | Label shown above the suggestions list (e.g. 'Recent searches', 'Results'). Empty string hides the header. |
| footerLabel | string | '' | Text displayed at the bottom of the dropdown — useful for "View all results". Empty string hides the footer. |
| groupByCategory | boolean | false | Groups the dropdown into labelled sections by each suggestion's `category` (e.g. "Pages", "People", "Tags"). Pre-sort `suggestions` so same-category items are contiguous — a header renders before each new run. Keyboard navigation and selection are unaffected; this only changes the headers rendered between rows. |
| filters | SearchFilter[] | [] | Active filter chips rendered inside the input track. Each chip has an × button that emits `filterRemoved`. |
| size | SearchBarSize | 'md' | Control height, text and icon scale: `'sm'` (32px), `'md'` (40px) or `'lg'` (48px). |
| variant | SearchBarVariant | 'default' | Surface style: `'default'` (bordered), `'filled'` (grey), `'borderless'` (transparent) or `'floating'` (bordered, with `label` floating above the text). |
| showSearchIcon | boolean | true | Show the magnifier icon on the left. |
| loading | boolean | false | Show the spinning loader inside the search icon area. |
| stale | boolean | false | The results on screen answer an **older** query — a newer request is still in flight. Distinct from `loading`, which cannot separate the two cases it covers: the first fetch (nothing on screen — show a skeleton) and a refinement of results already visible (keep them, and say they are provisional). Blanking the panel on the second is the most jarring thing a picker can do; showing the old list with no indication is a quiet lie. Bind `svSuggestController(...).stale` from `@styloviz/data`, or `createRequestGuard().stale` from `@styloviz/core`. The results dim and report `aria-busy` while staying fully interactive. |
| emptyReason | SvEmptyReason | null | null | Why the suggestion list is empty, or `null` when it is not empty (or when nothing has been asked yet and the panel should stay shut). Bind `svSuggestController(...).emptyReason` from `@styloviz/data`, which already distinguishes these two and holds `null` while a first load is in flight — an "opening" message that flashes over a search in progress is a third wrong answer. Leaving this unbound keeps the pre-existing behaviour exactly: an empty `suggestions` array closes the panel and says nothing. |
| noDataLabel | string | 'Type to search' | Copy for the opening state — nothing searched for yet. |
| noResultsLabel | string | 'No results found' | Copy for a search that answered with nothing. |
| noResultsHint | string | '' | A second line under `noResultsLabel` — "Check your spelling or try a broader term". Empty hides it. |
| disabled | boolean | false | Whether the input is disabled. |
| debounceMs | number | 300 | Debounce delay in milliseconds before `searched` emits. Set to 0 to emit on every keystroke. |
| customClass | string | '' | Additional classes on the root wrapper. |
Outputs
| Event | Payload type | Description |
|---|---|---|
| retry | void | The host should re-run the failed suggestion lookup. |
| searched | string | Emitted (debounced) on every value change. |
| submitted | string | Emitted when the user presses Enter or clicks a suggestion. |
| suggestionSelected | SearchSuggestion | Emitted when a suggestion row is clicked, carrying the suggestion object. |
| filterRemoved | string | Emitted when the × on a filter chip is clicked. |
| footerClick | void | Emitted when the footer link is clicked. |
| cleared | void | Emitted when the input is cleared via the × button. |
| valueChange | string | Emits the new `value` whenever it changes — the output half of `[(value)]`. |
Wire it to your API
This component is headless about transport: it renders what you give it and reports what the user asks for. Nothing below requires @styloviz/data — drive it from whatever you already use.
Template
Bound to the names this component actually exposes.
<sv-search-bar [suggestions]="data.items()" [loading]="data.loading()" [error]="data.error()" (retry)="data.reload()" />
Host class
Drive the three async inputs from your own fetch.
// This component reports its state but does not emit a query, so drive // it from your own fetch and bind the three async inputs. readonly items = signal<Row[]>([]); readonly loading = signal(false); readonly error = signal<SvDataError | null>(null); async load(): Promise<void> { this.loading.set(true); this.error.set(null); try { this.items.set(await this.api.fetchRows()); } catch (e) { this.error.set(toSvDataError(e)); } finally { this.loading.set(false); } }
- This component does not emit a query, so it renders what you give it. Fetch on your own trigger and bind `suggestions`, `loading` and `error`.
- `debounceMs` (default 300) collapses a burst of typing into one request. Set it to 0 only when the query is driven by clicks.
- Normalize failures with `toSvDataError()` from `@styloviz/core`. It derives `retryable` from the status, so a 404 does not get a "Try again" button that can only fail again.
Full walkthrough: Connecting to a backend.
Customization & theming
- Provide `suggestions` (with icon type, sublabel and badge) and removable `filters` chips; add a `suggestionsLabel` header and `footerLabel` action.
- Pick a `variant` (default · filled · borderless · floating) and `size`; toggle `showSearchIcon`, add a `shortcutHint` and a `loading` spinner.
- Section the dropdown with `groupByCategory` — set each suggestion's `category` (pre-sorted so same-category rows are contiguous) and a header renders before every new run.
- Tune `placeholder`, `debounceMs` and `ariaLabel`; react to `searched`, `submitted`, `suggestionSelected`, `filterRemoved`, `footerClick` and `cleared`.
Best practices
- Do: Debounce input (`debounceMs`) so you don't fire a request on every keystroke.
- Do: Show a `loading` state while results stream in.
- Don't: Don't block typing while fetching — keep the field responsive.
Accessibility
- Correct ARIA combobox: `role="combobox"` sits on the `<input>` with `aria-expanded`, `aria-haspopup`, `aria-controls` and `aria-activedescendant`; the popup is a `role="listbox"` of `role="option"` rows.
- All ids are generated per instance, so multiple search bars on one page don't collide; arrow keys move the highlighted option, Enter selects, Escape closes and returns focus.
- The accessible name comes from `ariaLabel` → `label` → `placeholder`, and `aria-busy` is set while `loading`.
Feedback on Search Bar
Spotted a gap in this page, or something that didn't work as described? Comments are GitHub Discussions in the public styloviz/community repo, so posting needs a GitHub account.