Combobox
A keyboard-accessible autocomplete combobox built on Base UI: type-ahead filtering, async options, and full ARIA, styled entirely with CSS classes.
Real use · searchable state picker
Docs search (no clear button)
Sizes
Using Combobox
Silica ships one design system through three paths. They share a class vocabulary but are not interchangeable — pick the one that matches what you are building.
Plain HTML — CSS classes
The Tailwind v4 plugin. Works in any framework or none — Rails, Django, PHP, Go, a static file. This path ships no JavaScript, so Combobox is styled but inert: its behavior comes from combobox. For that behavior with no framework, use the node-tree path below.
/* in your CSS */
@import "tailwindcss";
@plugin "@wizeworks/silicaui";Root class combobox-clear, with 5 modifiers:
.combobox-clear.combobox-control.combobox-empty.combobox-input.combobox-item.combobox-trigger
React
Typed components over those same classes, with behavior and accessibility from Base UI.
npm i @wizeworks/silicaui-reactimport { Combobox } from "@wizeworks/silicaui-react";Props
ComboboxItemProps
| Prop | Type | Required | Description |
|---|---|---|---|
| value | unknown | yes | — |
| children | React.ReactNode | no | — |
| indicator | boolean | no | Show the leading selected-check (default true). |
ComboboxProps
| Prop | Type | Required | Description |
|---|---|---|---|
| items | readonly unknown[] | yes | The full option set (strings or { value, label }); Base UI filters it. |
| value | unknown | no | Controlled selected value. |
| defaultValue | unknown | no | Uncontrolled initial value. |
| onValueChange | (value: unknown, eventDetails?: unknown) => void | no | Fires with the newly-selected value. |
| name | string | no | — |
| disabled | boolean | no | — |
| required | boolean | no | — |
| placeholder | string | no | — |
| emptyMessage | React.ReactNode | no | Shown in the popup when nothing matches the query. |
| color | ComboboxColor | no | Accent for the input border + focus ring (shares Input colors). |
| size | ComboboxSize | no | Input height; matches same-size Inputs. |
| clearable | boolean | no | Show the clear (×) button (default true). |
| side | ComboboxSide | no | — |
| align | ComboboxAlign | no | — |
| sideOffset | number | no | — |
| className | string | no | Class for the text input. |
| popupClassName | string | no | Class for the popup surface. |
| popupProps | React.ComponentProps<typeof BaseCombobox.Popup> & { "data-theme"?: string; } | no | Extra props spread onto the popup surface — a portaled popup renders at document.body, OUTSIDE any [data-theme] island it was opened from, so a host with a scoped theme (e.g. a builder chrome) should pass popupProps={{ "data-theme": "…" }} to re-establish the theme tokens on the popup's own root (mirrors Select's popupProps). |
| itemToStringLabel | (item: unknown) => string | no | For object items, map an item to its display string (default: .label). |
| renderItem | (item: unknown, index: number) => React.ReactNode | no | Override how each filtered item renders. |
| filter | (item: unknown, query: string) => boolean | no | Decide whether an item matches what was typed. By default an item matches when its label contains the query. |
| "aria-label" | string | no | — |
| "aria-labelledby" | string | no | — |
| id | string | no | — |
| anchor | BasePositionerProps["anchor"] | no | What to position against. Defaults to the component's own trigger. |
| positionMethod | BasePositionerProps["positionMethod"] | no | absolute (default) or fixed. Use fixed to escape a clipping ancestor. |
| alignOffset | BasePositionerProps["alignOffset"] | no | Offset along the alignment axis, in px. Pairs with align. |
| collisionBoundary | BasePositionerProps["collisionBoundary"] | no | The element the popup must stay inside. Defaults to the viewport — set it to a scroll container so the popup collides with the panel, not the window. |
| collisionPadding | BasePositionerProps["collisionPadding"] | no | Inset from the collision boundary, in px. |
| collisionAvoidance | BasePositionerProps["collisionAvoidance"] | no | Which collision strategy runs per axis (flip, shift, none). |
| sticky | BasePositionerProps["sticky"] | no | Keep the popup glued to the anchor while it scrolls out of view. |
| arrowPadding | BasePositionerProps["arrowPadding"] | no | Minimum distance from the popup's corner to the arrow, in px. |
| disableAnchorTracking | BasePositionerProps["disableAnchorTracking"] | no | Stop re-measuring when the anchor moves. Cheaper, but goes stale. |
Source of the demo above
The exact file that renders the examples above, so it can never drift from them. It imports a few of this site’s own layout helpers (../lib/…), so read it for the component calls rather than copying it whole.
Show source
import { useState } from "react";
import { Combobox } from "@wizeworks/silicaui-react";
import { Section, Row } from "../lib/Section";
const US_STATES = [
"Alabama", "Alaska", "Arizona", "Arkansas", "California", "Colorado",
"Connecticut", "Delaware", "Florida", "Georgia", "Hawaii", "Idaho",
"Illinois", "Indiana", "Iowa", "Kansas", "Kentucky", "Louisiana",
];
const DOC_SUGGESTIONS = [
"Getting started", "Installation", "Theming", "Color tokens",
"Dark mode", "Components", "Button", "Select", "Combobox",
"Form validation", "Accessibility", "Migration guide",
];
export function ComboboxDemo() {
const [usState, setUsState] = useState<string | null>(null);
const [docQuery, setDocQuery] = useState("");
return (
<>
<Section title="Real use · searchable state picker">
<Combobox
items={US_STATES}
value={usState}
onValueChange={(v) => setUsState(v as string | null)}
placeholder="Search states…"
color="primary"
/>
</Section>
<Section title="Docs search (no clear button)">
<Combobox
items={DOC_SUGGESTIONS}
value={docQuery || null}
onValueChange={(v) => setDocQuery((v as string) ?? "")}
placeholder="Search docs…"
clearable={false}
/>
</Section>
<Section title="Sizes">
<Row>
{(["sm", "md", "lg"] as const).map((size) => (
<Combobox key={size} items={["A", "B", "C"]} size={size} placeholder={size} />
))}
</Row>
</Section>
</>
);
}Vanilla — node tree to HTML
combobox — Combobox/Autocomplete/MultiSelect are all SINGLE, self- contained React components with an items prop (like Select, not a Root/Trigger/Content compound tree), so their vanilla macros follow the same shape: build the whole input+popup+options structure from props.items (unauthored structural sugar, same precedent as Carousel's invented track/dots). Build a node tree, project it with toHtml(), and let the zero-dependency runtime hydrate it. This is the path for generated or user-authored documents — a site builder, CMS output, a static export.
npm i @wizeworks/silicaui-html @wizeworks/silicaui-behaviorsimport { atom, toHtml } from "@wizeworks/silicaui-html";
const node = atom("Combobox", "combobox-clear", { /* props */ });
const markup = toHtml(node);Styling on this path is the class string in the second argument — the same classes as the Plain HTML section above. There is no color prop here; write "combobox-clear combobox-clear-primary" rather than passing a colour as a prop.
- Takes children
- no
- Hydrated by
combobox
Load @wizeworks/silicaui-behaviors on the page or this markup renders correctly and does nothing.
The full API for Combobox, on every path, is available through the Silica MCP server.