Combobox
A searchable select component for filtering and choosing from a list of options.
Areia’s Combobox is built on @data-slot/combobox and rendered as an Ilha island when you use Combobox. It supports keyboard navigation, filtering, form integration, disabled options, grouped items, and field helpers for labels, descriptions, and errors.
import { ilha } from "ilha";
import { Combobox } from "areia";
export default ilha.render(() => (
<Combobox
id="fruit"
label="Fruit"
placeholder="Search fruit..."
items={{
apple: "Apple",
banana: "Banana",
cherry: "Cherry",
date: "Date",
elderberry: "Elderberry",
}}
/>
));Import
import { Combobox } from "areia";
Usage
Call Combobox(...) for an interactive island. The root renders the combobox markup and initializes the @data-slot/combobox controller after mount.
import { ilha } from "ilha";
import { Combobox } from "areia";
export default ilha.render(() => (
<Combobox
id="ilha-topic"
label="Ilha topic"
placeholder="Search Ilha topics..."
items={{
islands: "Islands",
signals: "Signals",
jsx: "JSX rendering",
html: "html literals",
hydration: "Hydration",
}}
/>
));For static server-rendered markup without mounting behavior, use Combobox.Static(...). For most application usage, call Combobox(...).
Examples
Basic
A combobox with a visible label and searchable options.
import { ilha } from "ilha";
import { Combobox } from "areia";
export default ilha.render(() => (
<Combobox
id="basic-fruit"
label="Fruit"
placeholder="Choose a fruit"
items={{
apple: "Apple",
banana: "Banana",
cherry: "Cherry",
date: "Date",
elderberry: "Elderberry",
}}
/>
));Sizes
Use the size prop to match Input sizing: xs, sm, base, and lg.
import { ilha } from "ilha";
import { Combobox } from "areia";
const items = {
apple: "Apple",
banana: "Banana",
cherry: "Cherry",
};
export default ilha.render(() => (
<div class="flex w-full max-w-sm flex-col gap-3">
<Combobox
size="xs"
placeholder="Extra small"
items={items}
/>
<Combobox size="sm" placeholder="Small" items={items} />
<Combobox size="base" placeholder="Base" items={items} />
<Combobox size="lg" placeholder="Large" items={items} />
</div>
));With Description
The label, description, and error props enable the built-in field wrapper.
Choose the region closest to your users.
import { ilha } from "ilha";
import { Combobox } from "areia";
export default ilha.render(() => (
<Combobox
id="region"
label="Region"
description="Choose the region closest to your users."
placeholder="Search regions..."
items={{
iad: "Washington, D.C.",
sfo: "San Francisco",
lhr: "London",
fra: "Frankfurt",
nrt: "Tokyo",
}}
/>
));With Error
Pass error as a string for simple validation messages. Error styling is automatically applied when error is truthy.
import { ilha } from "ilha";
import { Combobox } from "areia";
export default ilha.render(() => (
<Combobox
id="database-error"
label="Database"
required
error="Select a database before continuing."
placeholder="Search databases..."
items={{
postgres: "PostgreSQL",
mysql: "MySQL",
mongodb: "MongoDB",
redis: "Redis",
}}
/>
));Disabled
Set disabled to prevent interaction.
import { ilha } from "ilha";
import { Combobox } from "areia";
export default ilha.render(() => (
<Combobox
label="Fruit"
placeholder="Search fruit..."
disabled
items={{
apple: "Apple",
banana: "Banana",
cherry: "Cherry",
}}
/>
));Disabled Items
Use item descriptor objects to disable individual options. Disabled rows are skipped during keyboard navigation and cannot be selected.
import { ilha } from "ilha";
import { Combobox } from "areia";
export default ilha.render(() => (
<Combobox
id="plans"
label="Plan"
placeholder="Search plans..."
items={{
free: "Free",
pro: "Pro",
business: { label: "Business", disabled: true },
enterprise: { label: "Enterprise", disabled: true },
}}
/>
));Default Value
Use defaultValue with the option value string to preselect an item.
import { ilha } from "ilha";
import { Combobox } from "areia";
export default ilha.render(() => (
<Combobox
id="default-database"
label="Database"
defaultValue="postgres"
items={{
postgres: "PostgreSQL",
mysql: "MySQL",
mongodb: "MongoDB",
redis: "Redis",
}}
/>
));Multiple Selection
Set multiple to allow selecting several values. Selected options render as removable Badge chips inside the input. Clicking an item toggles its selection and keeps the popup open. The typed search text clears after each selection, and Backspace with an empty input removes the last selected value. defaultValue accepts an array, and onValueChange receives a string[].
import { ilha } from "ilha";
import { Combobox } from "areia";
export default ilha.render(() => (
<Combobox
id="multi-frameworks"
label="Frameworks"
multiple
placeholder="Search frameworks..."
defaultValue={["astro", "svelte"]}
items={{
astro: "Astro",
next: "Next.js",
nuxt: "Nuxt",
remix: "Remix",
svelte: "SvelteKit",
}}
/>
));With name, one hidden input is created per selected value, so a native form submits fruits=apple&fruits=banana.
The committed selection can be bound with bind:value (an alias for bind:group). Types discriminate on multiple: in multiple mode bind:value accepts a string[] signal, defaultValue is string[], and onValueChange receives string[]; in single mode they stay string-typed.
Form Integration
Set name to create a hidden input that submits the selected value with a native form.
import { ilha } from "ilha";
import { Button, Combobox } from "areia";
export default ilha.render(() => (
<form class="flex w-full max-w-sm flex-col gap-3">
<Combobox
id="form-fruit"
name="fruit"
label="Fruit"
required
placeholder="Search fruit..."
items={{
apple: "Apple",
banana: "Banana",
cherry: "Cherry",
}}
/>
<Button type="submit">Submit</Button>
</form>
));Custom Items
Use explicit child markup when you need custom labels, grouping, separators, or item attributes.
import { ilha } from "ilha";
import { Combobox } from "areia";
export default ilha.render(() => (
<Combobox
id="custom-items"
label="Runtime"
placeholder="Search runtimes..."
>
<Combobox.Item value="bun" label="Bun">
<span class="font-medium">Bun</span>
<span class="text-areia-subtle"> JavaScript runtime</span>
</Combobox.Item>
<Combobox.Item value="node" label="Node.js">
<span class="font-medium">Node.js</span>
<span class="text-areia-subtle"> JavaScript runtime</span>
</Combobox.Item>
<Combobox.Item value="deno" label="Deno">
<span class="font-medium">Deno</span>
<span class="text-areia-subtle"> TypeScript runtime</span>
</Combobox.Item>
</Combobox>
));Grouped Items
Use Combobox.Group and Combobox.GroupLabel to organize related options.
import { ilha } from "ilha";
import { Combobox } from "areia";
export default ilha.render(() => (
<Combobox
id="grouped-location"
label="Location"
placeholder="Search locations..."
>
<Combobox.Group>
<Combobox.GroupLabel>North America</Combobox.GroupLabel>
<Combobox.Item value="iad">
Washington, D.C.
</Combobox.Item>
<Combobox.Item value="sfo">San Francisco</Combobox.Item>
</Combobox.Group>
<Combobox.Group>
<Combobox.GroupLabel>Europe</Combobox.GroupLabel>
<Combobox.Item value="lhr">London</Combobox.Item>
<Combobox.Item value="fra">Frankfurt</Combobox.Item>
</Combobox.Group>
</Combobox>
));Search Input Inside Popup
Use the low-level parts to render a select-like trigger with a separate search input inside the popup. This composition returns static markup; initialize it with createCombobox yourself if you are not using Combobox.
import { ilha } from "ilha";
import { Combobox } from "areia";
const items = [
<Combobox.Item value="go" label="Go" children="🐹 Go" />,
<Combobox.Item
value="rust"
label="Rust"
children="🦀 Rust"
/>,
<Combobox.Item
value="ts"
label="TypeScript"
children="🔷 TypeScript"
/>,
];
export default ilha.render(() => (
<Combobox
id="language-inside"
label="Language"
placeholder="Select language"
>
<Combobox.TriggerValue placeholder="Select language" />
<Combobox.Content>
<Combobox.Input placeholder="Search languages..." />
<Combobox.List>
<Combobox.Empty />
{items}
</Combobox.List>
</Combobox.Content>
</Combobox>
));Open on Focus and Auto Highlight
openOnFocus opens the popup when the input receives intentional focus. autoHighlight highlights the first matching item after typing.
import { ilha } from "ilha";
import { Combobox } from "areia";
export default ilha.render(() => (
<Combobox
id="quick-search"
label="Quick search"
placeholder="Start typing..."
openOnFocus
autoHighlight
items={{
dashboard: "Dashboard",
settings: "Settings",
billing: "Billing",
support: "Support",
}}
/>
));Autocomplete Behavior
Combobox supports autocomplete-style filtering natively. As the user types, the dropdown filters visible items automatically using the built-in substring matcher. Provide items and the controller handles narrowing without re-rendering.
import { ilha } from "ilha";
import { Combobox } from "areia";
export default ilha.render(() => (
<Combobox
id="autocomplete"
label="Language"
placeholder="Start typing..."
openOnFocus
items={{
rust: "Rust",
ruby: "Ruby",
react: "React",
python: "Python",
php: "PHP",
perl: "Perl",
}}
/>
));For a custom filter, pass a filter function instead of re-rendering with filtered items.
filter={(inputValue, itemValue, itemLabel) =>
itemLabel.toLowerCase().startsWith(inputValue.toLowerCase())
}
Use onInputValueChange to read the typed text for side effects like remote fetching. Do not use it to drive re-renders of the item list — the controller already filters internally.
For free-form values that do not need to match the list, use itemToStringValue to control how the selected text is displayed, or treat the input value as the committed value when the user submits the form without selecting an item.
Dropdown Height
Combobox.Content defaults to 24rem or the available viewport height, whichever is smaller. Pass class or className when composing the low-level content yourself.
import { ilha } from "ilha";
import { Combobox } from "areia";
const items = {
one: "One",
two: "Two",
three: "Three",
four: "Four",
five: "Five",
};
export default ilha.render(() => (
<Combobox
id="short-dropdown"
label="Short dropdown"
placeholder="Search..."
>
<Combobox.TriggerInput placeholder="Search..." />
<Combobox.Content class="max-h-40">
<Combobox.List>
<Combobox.Empty />
{Object.entries(items).map(([value, label]) => (
<Combobox.Item value={value}>{label}</Combobox.Item>
))}
</Combobox.List>
</Combobox.Content>
</Combobox>
));API Reference
Combobox
Combobox is an Ilha island. It accepts standard HTML div attributes plus @data-slot/combobox options and Areia field props.
| Prop | Type | Default | Description |
|---|---|---|---|
class |
string |
- | Additional CSS classes applied to the combobox root. |
className |
string |
- | Alias for class. |
size |
"xs" | "sm" | "base" | "lg" |
"base" |
Size of the trigger input. Matches Input component sizes. |
inputSide |
"right" | "top" |
"right" |
Styling hook for chip/input layouts. |
label |
unknown |
- | Label content. Enables the field wrapper. |
labelTooltip |
string |
- | Tooltip text rendered as a native title on the label. |
description |
unknown |
- | Helper text displayed below the combobox. |
error |
unknown | { message: unknown; match?: unknown } |
- | Error message. When truthy, error styling is automatically applied. |
items |
Record<string, unknown | { label: unknown; disabled?: boolean }> | ComboboxItemDescriptor[] |
- | Items rendered as selectable options. Object keys are option values. |
children |
unknown |
- | Explicit combobox content. Prefer the second Combobox argument. |
placeholder |
string |
- | Placeholder text for the trigger input or trigger value. |
multiple |
boolean |
false |
Allow selecting multiple values. Items toggle and the popup stays open. |
defaultValue |
string | string[] |
- | Initial selected value(s). Array form is for multiple mode. |
defaultOpen |
boolean |
false |
Initial open state. |
disabled |
boolean |
false |
Disable interaction. |
required |
boolean |
false |
Mark the field as required. When false with a label, shows optional text. |
name |
string |
- | Form field name. Creates a hidden input for submission. |
openOnFocus |
boolean |
true |
Open the popup when the input receives intentional focus. |
autoHighlight |
boolean |
false |
Auto-highlight the first visible item after typing. |
filter |
(inputValue: string, itemValue: string, itemLabel: string) => boolean |
- | Custom filter function. Return true to show an item. |
itemToStringValue |
(item: HTMLElement | null, value: string | null) => string |
- | Custom selected-value text resolver. |
onValueChange |
(value: string | string[] | null) => void |
- | Called when selection changes. Receives string[] in multiple mode. |
onOpenChange |
(open: boolean) => void |
- | Called when popup opens or closes. |
onInputValueChange |
(inputValue: string) => void |
- | Called when the user types in the input. |
side |
"top" | "bottom" |
"bottom" |
Preferred popup side. |
align |
"start" | "center" | "end" |
"start" |
Popup alignment relative to the trigger. |
sideOffset |
number |
4 |
Distance from the trigger in pixels. |
alignOffset |
number |
0 |
Offset from the aligned edge in pixels. |
avoidCollisions |
boolean |
true |
Adjust popup position to stay inside the viewport. |
collisionPadding |
number |
8 |
Viewport edge padding used for collision detection. |
Combobox.Item
Individual selectable option.
| Prop | Type | Default | Description |
|---|---|---|---|
value |
string |
- | Submitted and selected value for the item. |
children |
unknown |
- | Visible item content. |
label |
string |
- | Text used for filtering when children are custom. |
disabled |
boolean |
- | Whether the item cannot be selected. |
class |
string |
- | Additional CSS classes. |
className |
string |
- | Alias for class. |
Additional Sub-components
| Component | Description |
|---|---|
Combobox.TriggerInput |
Input trigger with clear and dropdown buttons. |
Combobox.TriggerValue |
Button trigger that displays the selected value. |
Combobox.Content |
Popup container for the input and list. |
Combobox.Input |
Search input for popup-input composition. |
Combobox.List |
Scrollable list wrapper. |
Combobox.Empty |
Empty state shown when no items match. |
Combobox.Group |
Group container for related items. |
Combobox.GroupLabel |
Visible label for a group. |
Combobox.Separator |
Visual separator between groups/items. |
Combobox.Chip |
Visual chip helper for custom multi-value interfaces. |
Accessibility
Keyboard Navigation
| Key | Action |
|---|---|
ArrowDown |
Open the popup when closed, or move to the next visible item. |
ArrowUp |
Open the popup when closed, or move to the previous item. |
Home |
Move to the first visible item. |
End |
Move to the last visible item. |
Enter |
Select the highlighted item. |
Escape |
Close the popup, or clear the selected value(s) when closed. |
Backspace |
In multiple mode with an empty input, remove the last selected value. |
Tab |
Close the popup and continue normal tab navigation. |
Label Requirement
Comboboxes should have an accessible name via one of:
labelproparia-labelon a customCombobox.TriggerInputaria-labelledbyfor custom label association
Form Behavior
When name is provided, the controller creates a hidden input with the selected value so the combobox participates in native form submission. In multiple mode, one hidden input is created per selected value.
Error Association
When an id is provided, descriptions and error messages are automatically associated with the combobox field wrapper using aria-describedby.