SearchField
A search or filter input in a <search> landmark, with a magnifier, a clear button and an optional shortcut hint. Inside a <form method="get"> it filters with no JavaScript; inside a Field it is labelled, described and validated by the Field. It is also the top bar's search, as a real input.
Import
import { SearchField } from '@nizivo/react/search-field';
// Once per app (Getting started), after
// @nizivo/tokens/tokens.css and @nizivo/react/css/base.css and @nizivo/react/css/kbd.css
import '@nizivo/react/css/search-field.css';Static or interactive
Client. It always ships its client leaf, which reads its Field through FieldScope and runs the
clear button, so a page with a SearchField also loads FieldScope. The server HTML is a native
<search> around an <input type="search"> that works before hydration. How to
choose.
In a client app (a Vite or React Router single-page app, or a 'use client' file) it is an ordinary React component: event handlers, refs and state work as usual. In a client app.
Filtering with no JavaScript
Put it in a <form method="get"> and give it a name. Enter submits the form, so the query lands in
the URL (?q=stoneware); the server page reads it from its search params, filters, and passes it
back as defaultValue. The <search> element is already the search landmark, so the form needs no
role. In a client app, read onChange (or pass value and onChange) and filter as the user types.
Naming it
Inside a Field, the Field's label names the input. Outside one, pass aria-label or
aria-labelledby; with neither, the input is named "Search" (labels.label). The placeholder is not
a label.
The shortcut
shortcut="mod+k" draws the keys at the end of the field (⌘K on Apple platforms, Ctrl+K elsewhere,
through Kbd and ThemeScript's data-nz-platform) and announces them with aria-keyshortcuts
("Meta+K Control+K"). The hint hides while the field has a value. As with IconButton's shortcut,
the app binds the key: listen for it and call focus() on the field's ref. A shared hotkey registry
comes in a later release.
Size and states
40 px at size="md" (Figma's 42 is off the control scale), 32 at sm, 48 at lg. pending turns
the magnifier into a spinner while results load; announce the outcome where the results are. Its edge
is border/control, not Figma's border/subtle, whose 1.07:1 against the top bar left the field
barely visible.
Examples
Basic
Filtering with no JavaScript: in a <form method="get">, Enter submits the query as ?q=…, and the server page reads it back and passes it as defaultValue. Escape or the clear button empties a filled field.
import type { JSX } from 'react';
import { SearchField } from '@nizivo/react/search-field';
export function SearchFieldExample(): JSX.Element {
return (
<form method="get" style={{ maxInlineSize: 360 }}>
<SearchField name="q" placeholder="Search orders" defaultValue="stoneware" />
</form>
);
}
Top Bar
In the top bar: a real input (M49), 360 px wide, with its shortcut drawn at the end. The app binds the key (focus the field through its ref); the input announces it through aria-keyshortcuts.
import type { JSX } from 'react';
import { SearchField } from '@nizivo/react/search-field';
export function SearchFieldTopBarExample(): JSX.Element {
return (
<header
style={{
display: 'flex',
alignItems: 'center',
gap: 16,
paddingBlock: 12,
paddingInline: 16,
background: 'var(--nz-bg-canvas)',
}}
>
<form method="get" style={{ flex: '0 1 360px', minInlineSize: 0 }}>
<SearchField
name="q"
placeholder="Search orders, products, customers"
shortcut="mod+k"
labels={{ label: 'Search the store' }}
/>
</form>
</header>
);
}
States
32, 40 and 48 px, the heights of Input, Button and Select; pending and disabled.
import type { JSX } from 'react';
import { SearchField } from '@nizivo/react/search-field';
export function SearchFieldStatesExample(): JSX.Element {
return (
<div style={{ display: 'grid', gap: 12, maxInlineSize: 360 }}>
<SearchField size="sm" aria-label="Search, small" placeholder="Small" />
<SearchField aria-label="Search, medium" placeholder="Medium" />
<SearchField size="lg" aria-label="Search, large" placeholder="Large" />
<SearchField aria-label="Search customers" defaultValue="anna" pending />
<SearchField aria-label="Search archived orders" placeholder="Archived orders" disabled />
</div>
);
}
Field
Inside a Field: the label names it, and the description and error describe it.
import type { JSX } from 'react';
import { Field } from '@nizivo/react/field';
import { SearchField } from '@nizivo/react/search-field';
export function SearchFieldFieldExample(): JSX.Element {
return (
<div style={{ display: 'grid', gap: 16, maxInlineSize: 360 }}>
<Field label="Find a product" description="By title, SKU or barcode.">
<SearchField name="product" placeholder="e.g. Stoneware mug" />
</Field>
<Field label="Order number" error="Enter at least three characters.">
<SearchField name="order" defaultValue="10" />
</Field>
</div>
);
}
Props
Client component Renders data-slot="search-field" with the class nz-search-field. Props: SearchFieldProps. Source: src/search-field/search-field.client.tsx.
| Name | Type | Default | Description |
|---|---|---|---|
icon | ReactNode | – | Replaces the magnifier before the value (decorative, 16 px). |
invalid | boolean | – | An error message is presented for this field (§8.3). Inside a Field, the Field sets it; an explicit value here wins. |
labels | Partial<SearchFieldLabels> | – | |
pending | boolean | false | Results are on their way: the magnifier becomes a spinner and the root carries |
render | RenderProp<SearchFieldState> | – | Composition of the root, the |
shortcut | string | – | A keyboard shortcut such as |
size | ControlSize"sm""md""lg" | 'md' | 32 / 40 / 48 px, as Input, Button and Select (Figma's 42 is off the scale). |
- Also takes every attribute of
<input>exceptchildren,prefix,size,type. (Omit<ComponentPropsWithRef<"input">, "type" | "size" | "prefix" | "children">)
render takes an element, or, in client code, a function of the props and this state (SearchFieldState):
| Name | Type | Default | Description |
|---|---|---|---|
disabledrequired | boolean | – | |
filledrequired | boolean | – | The input has a value, so the clear button shows. |
invalidrequired | boolean | – | An error message is presented (the Field's, or the |
pendingrequired | boolean | – | |
readOnlyrequired | boolean | – | |
sizerequired | ControlSize"sm""md""lg" | – |
Other exports
SearchFieldLabelstypeimport type { SearchFieldLabels } from '@nizivo/react/search-field'SearchField's own strings (§16.4): serialisable, English defaults.
Name Type Default Description clearrequiredstring'Clear search'The clear button's name.
labelrequiredstring'Search'The input's name when nothing else names it: no Field,
aria-labeloraria-labelledby. The placeholder is not a label.SearchFieldPropstypeimport type { SearchFieldProps } from '@nizivo/react/search-field'The props of
SearchField, listed above.SearchFieldStatetypeimport type { SearchFieldState } from '@nizivo/react/search-field'The render state of
SearchField, listed above.
Data attributes and slots
Style parts by class (.nz-<slot>) and state by data attribute. Attribute meanings come from contracts/vocabulary.json.
| Slot | Rendered by | Data attributes |
|---|---|---|
search-field.nz-search-field | SearchFieldroot (takes render); interactive, focus: within |
|
search-field-clear.nz-search-field-clear | SearchFieldinner element (CSS only) | none |
search-field-control.nz-search-field-control | SearchFieldinner element (CSS only) | none |
search-field-icon.nz-search-field-icon | SearchFieldinner element (CSS only) | none |
search-field-shortcut.nz-search-field-shortcut | SearchFieldinner element (CSS only) | none |
CSS hooks
Set a hook on the component's class or any ancestor to restyle it without specificity games; leave it unset for the default shown.
| Hook | Default | Read as |
|---|---|---|
--nz-search-field-bg |
|
|
--nz-search-field-bg-disabled | --nz-bg-disabled |
|
--nz-search-field-border |
|
|
--nz-search-field-border-hover |
|
|
--nz-search-field-fg | --nz-text-primary |
|
--nz-search-field-fg-disabled | --nz-text-disabled |
|
--nz-search-field-font |
|
|
--nz-search-field-gap | --nz-space-2 |
|
--nz-search-field-height |
|
|
--nz-search-field-icon-fg | --nz-icon-secondary |
|
--nz-search-field-inline-size | auto |
|
--nz-search-field-placeholder-fg | --nz-text-tertiary |
|
--nz-search-field-radius |
|
|
Layers: nz.components. Classes: .nz-glyph-spinner, .nz-search-field, .nz-search-field-clear, .nz-search-field-control, .nz-search-field-icon, .nz-search-field-shortcut.
Tokens this stylesheet reads (33)
--nz-bg-disabled, --nz-bg-muted, --nz-bg-pressed, --nz-bg-subtle, --nz-bg-surface, --nz-border-control, --nz-border-control-hover, --nz-border-critical, --nz-border-subtle, --nz-border-width-default, --nz-border-width-focus, --nz-duration-fast, --nz-duration-spin, --nz-easing-standard, --nz-focus-halo, --nz-focus-halo-critical, --nz-font-body-l, --nz-font-body-m, --nz-icon-primary, --nz-icon-secondary, --nz-radius-full, --nz-radius-lg, --nz-radius-md, --nz-size-control-lg, --nz-size-control-md, --nz-size-control-sm, --nz-size-icon-lg, --nz-size-icon-sm, --nz-space-2, --nz-space-3, --nz-text-disabled, --nz-text-primary, --nz-text-tertiary
Accessibility
These parts are interactive. Each gets the focus outline and touch-action rule from @nizivo/react/css/base.css (the nz.base layer), so the floor survives a restyle:
search-field: draws the outline when the field inside it has focus.
Keyboard
| Keys | Action |
|---|---|
| Tab | Moves focus to the input. The whole box draws the focus outline; the clear button is not a Tab stop. |
| Enter | Submits the form the field belongs to (the browser's implicit submission), so a GET form puts the query in the URL. |
| Escape | Clears a filled field and goes no further, so an enclosing Dialog stays open; on an empty field it does nothing. |
Source: a native <input type="search"> (search-field.client.tsx); its keydown handler clears on Escape in every engine (Firefox has no native clear), and the clear button empties the field through the native value setter and an input event, so onChange reports it. The browser lane (test/browser/search-field.spec.tsx) checks the clear button, Escape, GET submission, form.reset(), the focus outline and inline validation.
Figma
Node ids from design/component-map.json, which nz gen writes from component.json. The links open the Nizivo Figma library, which may ask you to sign in.
- Canonical: Figma node
7275:14764 - Ghost masters: Figma node
755:888
RankedList
Top products, channels or regions as an ordered list, each row a label, its value and a bar relative to the leader. The bars are decorative; the values are printed.
SegmentedControl
A compact single choice between a few options, such as a chart's time range. Native radios inside labels, so it submits, resets and works before hydration. Inside a Field, give the Field group.