Search toolbar
The toolbar that sits above a list or table (the Figma Wrapper / Filter + Search). A left content slot holds either a scope Select plus a search field, or a FilterBar. An Actions cluster is pinned to the right: up to three outline buttons (import, refresh, export) and a list / grid view toggle. Each action renders only when its handler is provided, and the toggle only when onViewChange is set.
Live preview
import {
SearchToolbar,
ToolbarSearch,
} from "@/components/organisms/search-toolbar";
<SearchToolbar
view={view}
onViewChange={setView}
onImport={openImport}
onRefresh={refetch}
onExport={exportCsv}
>
<ToolbarSearch
scopes={[{ value: "payment", label: "Payment ID" }, /* … */]}
scope={scope}
onScopeChange={setScope}
value={query}
onChange={setQuery}
/>
</SearchToolbar>Anatomy
A full-width row, left content justified against a right-pinned Actions cluster. The left is a slot: compose ToolbarSearch (a scope Select + a search field) for the search variant, or drop a FilterBar in for the filter variant. The right cluster holds the action buttons and the segmented view toggle.
- 1Left content slot
The search or filter content. Pass a ToolbarSearch (scope Select + search field) or a FilterBar.
- 2Scope Select
In ToolbarSearch: a Select that scopes the search (Payment ID, Order ID, Customer). h-9 field, no shadow.
- 3Search field
In ToolbarSearch: an h-9 field with a leading MagnifyingGlass and a muted placeholder. No shadow, just border-input.
- 4Action buttons
Up to three outline sm buttons pinned right: import (FileArrowUp), refresh (ArrowClockwise), export (DownloadSimple). Each renders only when its handler is set.
- 5View toggle
A list / grid segmented ToggleGroup (variant=outline, attached — border + split radius). Renders only when onViewChange is set.
Variants
Two canonical shapes from the Figma: the search variant (a scope Select + search field on the left) and the filter variant (a FilterBar on the left). Both carry the same right-pinned Actions. The Actions cluster is optional, so a bare search or filter row is also valid.
ToolbarSearch on the left (scope Select + search field), with import / refresh / export and the view toggle on the right.
import {
SearchToolbar,
ToolbarSearch,
} from "@/components/organisms/search-toolbar";
<SearchToolbar
view={view}
onViewChange={setView}
onImport={openImport}
onRefresh={refetch}
onExport={exportCsv}
>
<ToolbarSearch
scopes={[{ value: "payment", label: "Payment ID" }, /* … */]}
scope={scope}
onScopeChange={setScope}
value={query}
onChange={setQuery}
/>
</SearchToolbar>A FilterBar on the left (Add filter + tags), with export and the view toggle on the right.
<SearchToolbar view={view} onViewChange={setView} onExport={exportCsv}>
<FilterBar filters={filters} onChange={setApplied} />
</SearchToolbar>Just the left content — omit the action handlers and onViewChange and the right cluster disappears.
<SearchToolbar>
<ToolbarSearch scopes={scopes} scope={scope} onScopeChange={setScope} />
</SearchToolbar>Import
SearchToolbar is the chrome; pass the left content as children and wire the actions you need. ToolbarSearch is the canonical search content. Composes the kit Button, Select and ToggleGroup.
import { SearchToolbar, ToolbarSearch } from "@/components/organisms/search-toolbar";Props
SearchToolbar renders the chrome + Actions; ToolbarSearch is the scope Select + search field.
SearchToolbar
| Prop | Type | Default | Description |
|---|---|---|---|
| children | ReactNode | — | Left content: a ToolbarSearch, a FilterBar, or anything. |
| onImport | () => void | — | Renders the import button (FileArrowUp) when set. |
| onRefresh | () => void | — | Renders the refresh button (ArrowClockwise) when set. |
| onExport | () => void | — | Renders the export button (DownloadSimple) when set. |
| importLabel / refreshLabel / exportLabel | string | 'Import' / 'Refresh' / 'Export' | Labels for the action buttons. |
| view | 'list' | 'grid' | 'list' | Current view for the toggle. |
| onViewChange | (view) => void | — | Renders the list/grid segmented toggle when set. |
| actions | ReactNode | — | Extra nodes appended to the right Actions cluster. |
| className | string | — | Merged onto the toolbar row via cn(). |
ToolbarSearch
| Prop | Type | Default | Description |
|---|---|---|---|
| scopes | { value, label }[] | — | Scope options for the left Select (Payment ID, Order ID…). Omit to hide the Select. |
| scope | string | — | Selected scope value. |
| onScopeChange | (value) => void | — | Fires when the scope Select changes. |
| placeholder | string | 'Search' | Search field placeholder. |
| value | string | — | Search query value. |
| onChange | (value) => void | — | Fires on search input. |
| className | string | — | Merged onto the wrapper via cn(). |
When to use
- Above any list or table that needs a search or filter row plus export / refresh / view controls.
- As the single toolbar row over Payments, Transactions, Reconciliations or Payouts.
- Whenever the same Actions (import / refresh / export / view) should sit consistently on the right.
When not to use
- As a page intro with a title and subtitle — that is PageHeader.
- As the publish toolbar of an editor — that is PublishBar.
- For the account / environment context over a preview — that is PreviewContextBar.
Usage
- Compose ToolbarSearch or FilterBar into the left slot; keep the Actions on the right.
- Render only the actions the screen actually supports — each button is handler-gated.
- Keep the view toggle as a segmented outline control (list first, pressed).
- Don't put the primary page action here — this is a list toolbar, not a page header.
- Don't drop the toggle's border — it is an outline segmented control, not ghost toggles.
- Don't stack two toolbars; one row carries the search / filter and its Actions.
Related
Cross-links to atoms and patterns you may reach for next.
- Filter barThe left content for the filter variant (Add filter + tags).
- SelectThe scope Select inside ToolbarSearch.
- Toggle groupThe list / grid segmented view toggle.
- InputThe search field pattern (here inlined with a leading icon).