Search Bar

Free

Command-palette style search with suggestions, filters and loading states.

</>sv-search-bar searchautocompletefilter
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 demo

More examples

With filters & shortcut hint

example.htmlAngular
<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.

example.htmlAngular
<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

NameTypeDefaultDescription
errorSvDataError | string | nullnullLoad failure. Use `toSvDataError()` from `@styloviz/core` to normalize an `HttpErrorResponse`.
valuestring (two-way)''Current input text. Use `[(value)]` for two-way binding.
labelstring''Floating label text — only shown when `variant="floating"`. Acts as a Material Design-style animated label.
placeholderstring'Search…'Placeholder text.
ariaLabelstring''Explicit accessible name for the input. Falls back to `label` then `placeholder`.
shortcutHintstring''Keyboard shortcut hint shown on the right side of the input when empty. E.g. `'⌘K'`, `'Ctrl+K'`, `'/''`. Empty string hides the badge.
suggestionsSearchSuggestion[][]Suggestion items to display in the dropdown. Pass a filtered/sliced array; the component renders all provided items. Empty array closes the dropdown.
suggestionsLabelstring''Label shown above the suggestions list (e.g. 'Recent searches', 'Results'). Empty string hides the header.
footerLabelstring''Text displayed at the bottom of the dropdown — useful for "View all results". Empty string hides the footer.
filtersSearchFilter[][]Active filter chips rendered inside the input track. Each chip has an × button that emits `filterRemoved`.
sizeSearchBarSize'md'Field size: sm, md or lg.
variantSearchBarVariant'default'Visual variant: default, floating, minimal…
showSearchIconbooleantrueShow the magnifier icon on the left.
loadingbooleanfalseShow the spinning loader inside the search icon area.
stalebooleanfalseThe 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.
emptyReasonSvEmptyReason | nullnullWhy 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.
noDataLabelstring'Type to search'Copy for the opening state — nothing searched for yet.
noResultsLabelstring'No results found'Copy for a search that answered with nothing.
noResultsHintstring''A second line under `noResultsLabel` — "Check your spelling or try a broader term". Empty hides it.
disabledbooleanfalseWhether the input is disabled.
debounceMsnumber300Debounce delay in milliseconds before `searched` emits. Set to 0 to emit on every keystroke.
customClassstring''Additional classes on the root wrapper.

Outputs

EventPayload typeDescription
retryvoidThe host should re-run the failed suggestion lookup.
searchedstringEmitted (debounced) on every value change.
submittedstringEmitted when the user presses Enter or clicks a suggestion.
suggestionSelectedSearchSuggestionEmitted when a suggestion row is clicked, carrying the suggestion object.
filterRemovedstringEmitted when the × on a filter chip is clicked.
footerClickvoidEmitted when the footer link is clicked.
clearedvoidEmitted when the input is cleared via the × button.
valueChangestringEmits 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

NameTypeDefaultDescription
errorSvDataError | string | nullnullLoad failure. Use `toSvDataError()` from `@styloviz/core` to normalize an `HttpErrorResponse`.
valuestring (two-way)''Current input text. Use `[(value)]` for two-way binding.
labelstring''Floating label text — only shown when `variant="floating"`. Acts as a Material Design-style animated label.
placeholderstring'Search…'Placeholder text.
ariaLabelstring''Explicit accessible name for the input. Falls back to `label` then `placeholder`.
shortcutHintstring''Keyboard shortcut hint shown on the right side of the input when empty. E.g. `'⌘K'`, `'Ctrl+K'`, `'/''`. Empty string hides the badge.
suggestionsSearchSuggestion[][]Suggestion items to display in the dropdown. Pass a filtered/sliced array; the component renders all provided items. Empty array closes the dropdown.
suggestionsLabelstring''Label shown above the suggestions list (e.g. 'Recent searches', 'Results'). Empty string hides the header.
footerLabelstring''Text displayed at the bottom of the dropdown — useful for "View all results". Empty string hides the footer.
groupByCategorybooleanfalseGroups 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.
filtersSearchFilter[][]Active filter chips rendered inside the input track. Each chip has an × button that emits `filterRemoved`.
sizeSearchBarSize'md'Control height, text and icon scale: `'sm'` (32px), `'md'` (40px) or `'lg'` (48px).
variantSearchBarVariant'default'Surface style: `'default'` (bordered), `'filled'` (grey), `'borderless'` (transparent) or `'floating'` (bordered, with `label` floating above the text).
showSearchIconbooleantrueShow the magnifier icon on the left.
loadingbooleanfalseShow the spinning loader inside the search icon area.
stalebooleanfalseThe 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.
emptyReasonSvEmptyReason | nullnullWhy 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.
noDataLabelstring'Type to search'Copy for the opening state — nothing searched for yet.
noResultsLabelstring'No results found'Copy for a search that answered with nothing.
noResultsHintstring''A second line under `noResultsLabel` — "Check your spelling or try a broader term". Empty hides it.
disabledbooleanfalseWhether the input is disabled.
debounceMsnumber300Debounce delay in milliseconds before `searched` emits. Set to 0 to emit on every keystroke.
customClassstring''Additional classes on the root wrapper.

Outputs

EventPayload typeDescription
retryvoidThe host should re-run the failed suggestion lookup.
searchedstringEmitted (debounced) on every value change.
submittedstringEmitted when the user presses Enter or clicks a suggestion.
suggestionSelectedSearchSuggestionEmitted when a suggestion row is clicked, carrying the suggestion object.
filterRemovedstringEmitted when the × on a filter chip is clicked.
footerClickvoidEmitted when the footer link is clicked.
clearedvoidEmitted when the input is cleared via the × button.
valueChangestringEmits 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.

usage.htmlAngular
<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.

host.component.tsTypeScript
// 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.

Loads giscus.app and GitHub — nothing is fetched until you click.