Combobox
A text input that suggests items from a list as the reader types, such as a country in a checkout form or search suggestions. Input's box, with Select's popup and rows; React Aria's ComboBox inside.
Import
import { Combobox } from '@nizivo/react/combobox';
// Once per app (Getting started), after
// @nizivo/tokens/tokens.css and @nizivo/react/css/base.css and @nizivo/react/css/input.css
import '@nizivo/react/css/combobox.css';Static or interactive
Client. It is React Aria's ComboBox, which reads its Field through FieldScope, so a page with a Combobox also loads FieldScope. The server HTML is the input with the chosen item's text and, with a name, the hidden input with its value, so a form submits before hydration; the list opens once the page has hydrated. 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.
Parts
Combobox.Root holds the value and draws the box. Combobox.Input is the text input;
Combobox.Trigger after it adds a chevron button that opens every item. Combobox.Popup holds
Combobox.Items, which may sit in Combobox.Groups with a Combobox.GroupLabel, apart from each
other by a Combobox.Separator. The popup is as wide as the box, the Trigger included.
Value and text
value is the chosen item's value, and inputValue the text in the input; each has a
default… and an on…Change. Choosing an item puts its text (textValue, or its children when they
are a string) in the input. Without allowsCustomValue, a blur or Escape puts back the chosen
item's text, and clearing the input clears the choice. With it, the text may be anything: it stays
as typed, and a form submits it.
When the list opens
menuTrigger is 'input' by default: the list opens as the reader types and shows the items that
contain the text, matched in the locale's collation and ignoring case. 'focus' also opens it,
with every item, when the input takes focus or is pressed. 'manual' opens it only from the
Trigger or the arrow keys. With no match the list closes. defaultOpen opens it on the first
render; React Aria keeps the open state itself, so there is no controlled open, and
onOpenChange reports each change.
Forms
With name (or a Field's), a hidden input carries the chosen item's value, de and never
"Germany", into FormData; with allowsCustomValue the input itself carries the typed text.
Inside a Field, required is checked natively on the input, so the Field's
messages.valueMissing applies.
Engine-owned
The Trigger's name, "Show suggestions" followed by the field's label, the list's name,
"Suggestions" (used only when the field has no label; with one, the list is named by it), and the
announcements of how many items are available come from React Aria's catalogue, translated in 36
locales. labels does not set them: the Strings guide lists the
engine-owned strings.
Examples
Basic
A country in a checkout form: type to filter; the form gets the chosen code.
import type { JSX } from 'react';
import { Field } from '@nizivo/react/field';
import { Combobox } from '@nizivo/react/combobox';
export function ComboboxExample(): JSX.Element {
return (
<Field name="country" label="Country" description="Where we ship the order." required>
<Combobox.Root>
<Combobox.Input placeholder="Start typing" />
<Combobox.Trigger />
<Combobox.Popup>
<Combobox.Item value="at">Austria</Combobox.Item>
<Combobox.Item value="be">Belgium</Combobox.Item>
<Combobox.Item value="de">Germany</Combobox.Item>
<Combobox.Item value="fr">France</Combobox.Item>
<Combobox.Item value="in">India</Combobox.Item>
<Combobox.Item value="nl">Netherlands</Combobox.Item>
<Combobox.Item value="gb">United Kingdom</Combobox.Item>
<Combobox.Item value="us">United States</Combobox.Item>
</Combobox.Popup>
</Combobox.Root>
</Field>
);
}
Suggestions
Search suggestions, grouped, with any text allowed: the form gets what was typed.
import type { JSX } from 'react';
import { Field } from '@nizivo/react/field';
import { Combobox } from '@nizivo/react/combobox';
export function ComboboxSuggestionsExample(): JSX.Element {
return (
<Field name="q" label="Search products">
<Combobox.Root allowsCustomValue>
<Combobox.Input placeholder="Mugs, bowls, glaze…" />
<Combobox.Popup>
<Combobox.Group>
<Combobox.GroupLabel>Recent</Combobox.GroupLabel>
<Combobox.Item value="sand-mug">Sand mug</Combobox.Item>
</Combobox.Group>
<Combobox.Separator />
<Combobox.Group>
<Combobox.GroupLabel>Categories</Combobox.GroupLabel>
<Combobox.Item value="mugs">Mugs</Combobox.Item>
<Combobox.Item value="bowls">Bowls</Combobox.Item>
<Combobox.Item value="plates">Plates</Combobox.Item>
</Combobox.Group>
</Combobox.Popup>
</Combobox.Root>
</Field>
);
}
States
A chosen value, sizes, an error from the server and disabled.
import type { JSX } from 'react';
import { Field } from '@nizivo/react/field';
import { Combobox } from '@nizivo/react/combobox';
export function ComboboxStatesExample(): JSX.Element {
const items = (
<Combobox.Popup>
<Combobox.Item value="standard">Standard</Combobox.Item>
<Combobox.Item value="express">Express</Combobox.Item>
<Combobox.Item value="pickup" disabled>
Store pickup
</Combobox.Item>
</Combobox.Popup>
);
return (
<div style={{ display: 'grid', gap: 16, maxInlineSize: 320 }}>
<Field label="Shipping">
<Combobox.Root defaultValue="express" size="sm">
<Combobox.Input />
<Combobox.Trigger />
{items}
</Combobox.Root>
</Field>
<Field label="Shipping" error="Express is not available for this address.">
<Combobox.Root defaultValue="express" size="lg">
<Combobox.Input />
<Combobox.Trigger />
{items}
</Combobox.Root>
</Field>
<Field label="Shipping" disabled>
<Combobox.Root defaultValue="standard">
<Combobox.Input />
<Combobox.Trigger />
{items}
</Combobox.Root>
</Field>
</div>
);
}
Parts
Use the parts through the namespace (Combobox.Root) or by their flat names (ComboboxRoot); both work from Server Components.
Combobox.Root
Client component Renders data-slot="combobox" with the class nz-combobox. Props: ComboboxRootProps. Source: src/combobox/combobox.client.tsx.
| Name | Type | Default | Description |
|---|---|---|---|
allowsCustomValue | boolean | false | The text may be any value, not only an item's: on blur it stays as typed, and a form submits the text instead of the chosen item's |
aria-label | string | – | |
aria-labelledby | string | – | Inside a Field, the Field label's id, which also names the list and the Trigger. |
childrenrequired | ReactNode | – |
|
className | string | – | |
defaultInputValue | string | – | |
defaultOpen | boolean | – | Open on the first render, showing every item. React Aria keeps the open state itself, so there is no controlled |
defaultValue | string | null | – | |
disabled | boolean | – | |
id | string | – | The input's id. Inside a Field, the Field's, which its label points at. |
inputValue | string | – | The text in the input (controlled). |
invalid | boolean | – | An error message is presented (owned by Field, never by the engine). Inside a Field, the Field sets it. |
menuTrigger | ComboboxMenuTrigger"input""focus""manual" | 'input' | |
name | string | – | With |
onInputChange | (inputValue: string) => void | – | Every change of the text: typed, cleared, or set to the chosen item's text. |
onOpenChange | (open: boolean, details: ChangeDetails<OpenReason>) => void | – |
|
onValueChange | (value: string | null, details: ChangeDetails<ComboboxValueReason>) => void | – |
|
readOnly | boolean | – | |
required | boolean | – | |
size | ControlSize"sm""md""lg" | 'md' | 32 / 40 / 48 px, as Input. |
value | string | null | – | The chosen item's |
Combobox.Input
Client component Renders data-slot="combobox-input" with the class nz-combobox-input. Props: ComboboxInputProps. Source: src/combobox/combobox.client.tsx.
| Name | Type | Default | Description |
|---|---|---|---|
render | RenderProp<ComboboxInputState> | – | Must render an |
- Also takes every attribute of
<input>exceptchildren,defaultValue,disabled,id,name,readOnly,required,size,type,value. (Omit<ComponentPropsWithRef<"input">, "children" | "value" | "defaultValue" | "type" | "size" | "id" | "name" | "required" | "disabled" | "readOnly">)
render takes an element, or, in client code, a function of the props and this state (ComboboxInputState):
| Name | Type | Default | Description |
|---|---|---|---|
disabledrequired | boolean | – | |
focusVisiblerequired | boolean | – | |
openrequired | boolean | – | The list is open ( |
Combobox.Trigger
Client component Renders data-slot="combobox-trigger" with the class nz-combobox-trigger. Props: ComboboxTriggerProps. Source: src/combobox/combobox.client.tsx.
| Name | Type | Default | Description |
|---|---|---|---|
children | ReactNode | – | Replaces the chevron glyph. Decorative. |
render | RenderProp<ComboboxTriggerState> | – | Must render a |
- Also takes every attribute of
<button>exceptchildren,disabled,type. (Omit<ComponentPropsWithRef<"button">, "children" | "disabled" | "type">)
render takes an element, or, in client code, a function of the props and this state (ComboboxTriggerState):
| Name | Type | Default | Description |
|---|---|---|---|
disabledrequired | boolean | – | |
openrequired | boolean | – | |
pressedrequired | boolean | – |
Combobox.Popup
Client component Renders data-slot="combobox-popup" with the class nz-combobox-popup. Props: ComboboxPopupProps. Source: src/combobox/combobox.client.tsx.
| Name | Type | Default | Description |
|---|---|---|---|
align | Align"start""center""end" | 'start' | |
childrenrequired | ReactNode | – | |
className | string | – | |
container | Element | null | – | Portal container. Defaults to the parent overlay, else |
side | "top" | "bottom" | 'bottom' | |
sideOffset | number | 4 | Distance from the box in px. |
style | CSSProperties | – |
Combobox.Item
Client component Renders data-slot="combobox-item" with the class nz-combobox-item. Props: ComboboxItemProps. Source: src/combobox/combobox.client.tsx.
| Name | Type | Default | Description |
|---|---|---|---|
childrenrequired | ReactNode | – | |
disabled | boolean | – | |
indicator | ReactNode | – | Replaces the check shown at the end of the chosen item; |
render | RenderProp<ComboboxItemState> | – | Must render a |
textValue | string | – | The text the input shows and the filter matches; defaults to |
valuerequired | string | – |
- Also takes every attribute of
<div>exceptchildren,id. (Omit<ComponentPropsWithRef<"div">, "children" | "id">)
render takes an element, or, in client code, a function of the props and this state (ComboboxItemState):
| Name | Type | Default | Description |
|---|---|---|---|
disabledrequired | boolean | – | |
focusVisiblerequired | boolean | – | |
highlightedrequired | boolean | – | Under the pointer, or the active item of the keyboard (the input keeps focus). |
selectedrequired | boolean | – |
Combobox.Group
Client component Renders data-slot="combobox-group" with the class nz-combobox-group. Props: ComboboxGroupProps. Source: src/combobox/combobox.client.tsx.
| Name | Type | Default | Description |
|---|---|---|---|
render | RenderProp<NoState> | – | Must render a |
- Also takes every attribute of
<section>exceptid. (Omit<ComponentPropsWithRef<"section">, "id">)
render takes an element, or, in client code, a function of the props and no state.
Combobox.GroupLabel
Client component Renders data-slot="combobox-group-label" with the class nz-combobox-group-label. Props: ComboboxGroupLabelProps. Source: src/combobox/combobox.client.tsx.
| Name | Type | Default | Description |
|---|---|---|---|
render | RenderProp<NoState> | – | Must render a |
- Also takes every attribute of
<header>exceptid. (Omit<ComponentPropsWithRef<"header">, "id">)
render takes an element, or, in client code, a function of the props and no state.
Combobox.Separator
Client component Renders data-slot="combobox-separator" with the class nz-combobox-separator. Props: ComboboxSeparatorProps. Source: src/combobox/combobox.client.tsx.
| Name | Type | Default | Description |
|---|---|---|---|
render | RenderProp<NoState> | – | Must render a |
- Also takes every attribute of
<div>exceptchildren. (Omit<ComponentPropsWithRef<"div">, "children">)
render takes an element, or, in client code, a function of the props and no state.
Other exports
ComboboxGroupLabelPropstypeimport type { ComboboxGroupLabelProps } from '@nizivo/react/combobox'The props of
Combobox.GroupLabel, listed above.ComboboxGroupPropstypeimport type { ComboboxGroupProps } from '@nizivo/react/combobox'The props of
Combobox.Group, listed above.ComboboxInputPropstypeimport type { ComboboxInputProps } from '@nizivo/react/combobox'The props of
Combobox.Input, listed above.ComboboxInputStatetypeimport type { ComboboxInputState } from '@nizivo/react/combobox'The render state of
Combobox.Input, listed above.ComboboxItemPropstypeimport type { ComboboxItemProps } from '@nizivo/react/combobox'The props of
Combobox.Item, listed above.ComboboxItemStatetypeimport type { ComboboxItemState } from '@nizivo/react/combobox'The render state of
Combobox.Item, listed above.ComboboxMenuTriggertypeimport type { ComboboxMenuTrigger } from '@nizivo/react/combobox'When the list opens.
'input': as the reader types (the default).'focus': also when the input takes focus, showing every item.'manual': only from the Trigger or Arrow Down / Up.type ComboboxMenuTrigger = "input" | "focus" | "manual"ComboboxPopupPropstypeimport type { ComboboxPopupProps } from '@nizivo/react/combobox'The props of
Combobox.Popup, listed above.ComboboxPopupStatetypeimport type { ComboboxPopupState } from '@nizivo/react/combobox'Name Type Default Description enteringrequiredboolean– exitingrequiredboolean– siderequiredSide"top""right""bottom""left"– ComboboxRootPropstypeimport type { ComboboxRootProps } from '@nizivo/react/combobox'The props of
Combobox.Root, listed above.ComboboxRootStatetypeimport type { ComboboxRootState } from '@nizivo/react/combobox'Name Type Default Description disabledrequiredboolean– invalidrequiredboolean– openrequiredboolean– readOnlyrequiredboolean– requiredrequiredboolean– ComboboxSeparatorPropstypeimport type { ComboboxSeparatorProps } from '@nizivo/react/combobox'The props of
Combobox.Separator, listed above.ComboboxTriggerPropstypeimport type { ComboboxTriggerProps } from '@nizivo/react/combobox'The props of
Combobox.Trigger, listed above.ComboboxTriggerStatetypeimport type { ComboboxTriggerState } from '@nizivo/react/combobox'The render state of
Combobox.Trigger, listed above.ComboboxValueReasontypeimport type { ComboboxValueReason } from '@nizivo/react/combobox'React Aria reports no cause for a change.
type ComboboxValueReason = "unknown"
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 |
|---|---|---|
combobox.nz-combobox | Combobox.Rootroot (no render) |
|
combobox-control.nz-combobox-control | Combobox.Rootinner element (CSS only); interactive, focus: within |
|
combobox-group.nz-combobox-group | Combobox.Grouproot (takes render) | none |
combobox-group-label.nz-combobox-group-label | Combobox.GroupLabelroot (takes render) | none |
combobox-input.nz-combobox-input | Combobox.Inputroot (takes render) |
|
combobox-item.nz-combobox-item | Combobox.Itemroot (takes render); interactive, focus: engine; forced colours: highlight on [data-selected] |
|
combobox-list.nz-combobox-list | Combobox.Popupinner element (CSS only) | none |
combobox-popup.nz-combobox-popup | Combobox.Popuproot (no render); forced colours: border |
|
combobox-separator.nz-combobox-separator | Combobox.Separatorroot (takes render) | none |
combobox-trigger.nz-combobox-trigger | Combobox.Triggerroot (takes render); interactive, focus: self |
|
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-combobox-trigger-fg | --nz-text-secondary |
|
Layers: nz.components. Classes: .nz-combobox, .nz-combobox-chevron, .nz-combobox-control, .nz-combobox-group, .nz-combobox-group-label, .nz-combobox-item, .nz-combobox-item-indicator, .nz-combobox-list, .nz-combobox-popup, .nz-combobox-separator, .nz-combobox-trigger, .nz-input. Runtime variables: --nz-available-height, --nz-transform-origin, --nz-trigger-width.
Tokens this stylesheet reads (34)
--nz-bg-brand-subtle, --nz-bg-hover, --nz-bg-pressed, --nz-bg-surface-raised, --nz-border-subtle, --nz-border-width-default, --nz-border-width-focus, --nz-duration-fade, --nz-duration-fast, --nz-duration-spring, --nz-easing-enter, --nz-easing-exit, --nz-easing-spring, --nz-easing-standard, --nz-elevation-4, --nz-font-label-m, --nz-font-label-m-strong, --nz-font-label-s, --nz-icon-brand, --nz-radius-md, --nz-radius-sm, --nz-scale-enter, --nz-size-icon-sm, --nz-space-1, --nz-space-1-5, --nz-space-2, --nz-space-3, --nz-space-6, --nz-text-disabled, --nz-text-primary, --nz-text-secondary, --nz-tracking-label-m, --nz-tracking-label-s, --nz-z-overlay
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:
combobox-control: draws the outline when the field inside it has focus.combobox-item: draws the focus outline when the engine reports keyboard focus, not on pointer hover.combobox-trigger: draws the focus outline itself on keyboard focus.
In forced-colours mode these states keep a visible system colour: combobox-item[data-selected] (highlight), combobox-popup (border).
Behaviour comes from the react-aria engine through Nizivo's adapter: focus management, ARIA wiring and keyboard handling are the engine's.
Keyboard
| Keys | Action |
|---|---|
| Tab | Moves focus to the input. The Trigger is skipped; Tab out of the input closes the list. |
| Typing | Opens the list with the items that contain the text, in any case. |
| Arrow Down, Arrow Up | Open the list, then move the active item. Focus stays in the input, which names the active item for a screen reader. |
| Enter | Chooses the active item and closes the list. |
| Escape | Closes the list and puts back the chosen item's text (with allowsCustomValue, the typed text stays). |
Source: React Aria Components 1.22.1 ComboBox (_engines/react-aria/combobox.client.tsx). The overlay lane (test/overlay/combobox.spec.tsx) runs the family's overlay conformance and checks filtering, the active item, Enter, Escape, the Trigger, blur, custom values, forms and a Field's required message.
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: none
- Ghost masters: none
ChoiceCard
A choice drawn as cards, such as a saved payment method or a delivery speed. Each ChoiceCard is a label around a real, visible native radio, with slots for a leading mark (a card brand), a second line and trailing content such as a "Default" Badge. ChoiceCardGroup is the radio group; inside a Field, give the Field group, and its error marks the chosen card invalid.
CommandMenu
Unstable: a searchable list of places and actions in a dialog, opened with ⌘K (Ctrl+K elsewhere) or a button, as Figma's Command Menu draws it. Import it from @nizivo/react/unstable/command-menu.