Fill in a React input field with autocomplete & autosuggest functionalities. Choose from a list of suggested values with full keyboard support. This component is based on the WAI-ARIA Combobox Pattern and is powered by the ariakit Combobox.
Use Combobox for a list where the user types to filter — large static lists, async/server-side data, or any single-select where search is helpful. For small finite lists without filtering, use Select. For multi-selection, use Multi Select.
Pair with Field.* for label/description/error wiring in forms — wrap Combobox.Input in Field.Control. Field.Control flows the generated id, name, aria-describedby, aria-errormessage, and aria-invalid onto the focusable input.
Start typing to filter available domains.
1import { Combobox } from "@ngrok/mantle/combobox";2import { Field } from "@ngrok/mantle/field";3 4<Field.Item name="subdomain">5 <Field.Label>Subdomain</Field.Label>6 <Combobox.Root>7 <Field.Control>8 <Combobox.Input placeholder="Choose an ngrok subdomain..." />9 </Field.Control>10 <Combobox.Content>11 <Combobox.Group>12 <Combobox.GroupLabel>Available domains</Combobox.GroupLabel>13 <Combobox.Item value="https://${random}.ngrok.app">14 <Combobox.ItemValue />15 </Combobox.Item>16 <Combobox.Item value="https://${random}.ngrok.dev">17 <Combobox.ItemValue />18 </Combobox.Item>19 <Combobox.Item value="https://${random}.ngrok.io">20 <Combobox.ItemValue />21 </Combobox.Item>22 </Combobox.Group>23 </Combobox.Content>24 </Combobox.Root>25 <Field.Description>Start typing to filter available domains.</Field.Description>26</Field.Item>;Or render the control on its own:
1import { Combobox } from "@ngrok/mantle/combobox";2import { CirclesThreePlusIcon } from "@phosphor-icons/react/CirclesThreePlus";3 4<Combobox.Root>5 <Combobox.Input />6 <Combobox.Content>7 <Combobox.Group>8 <Combobox.GroupLabel>Choose an ngrok subdomain</Combobox.GroupLabel>9 <Combobox.Item value="https://" disabled>10 <Combobox.ItemValue />11 </Combobox.Item>12 <Combobox.Item value="https://${random}.ngrok.app">13 <CirclesThreePlusIcon weight="duotone" className="text-accent-600" />14 <Combobox.ItemValue />15 </Combobox.Item>16 <Combobox.Item value="https://${random}.ngrok.dev">17 <Combobox.ItemValue />18 </Combobox.Item>19 <Combobox.Item value="https://${random}.ngrok.io">20 <Combobox.ItemValue />21 </Combobox.Item>22 </Combobox.Group>23 <Combobox.Separator />24 <Combobox.Item>25 Sit dolor enim eiusmod nulla nostrud officia in magna deserunt ut ex veniam cillum.26 </Combobox.Item>27 </Combobox.Content>28</Combobox.Root>;Use @tanstack/react-form with zod to keep the combobox input controlled. Wrap Combobox.Input in Field.Control so label, name, and validation ARIA are applied to the focusable input.
1import { Button } from "@ngrok/mantle/button";2import { Combobox } from "@ngrok/mantle/combobox";3import { Field, toErrorMessages } from "@ngrok/mantle/field";4import { useForm } from "@tanstack/react-form";5import { z } from "zod";6 7export const formSchema = z.object({8 region: z.string().min(1, "Choose a region."),9});10function Example() {11 const defaultValues = {12 region: "",13 };14 const form = useForm({15 defaultValues,16 validators: {17 onSubmit: formSchema,18 },19 onSubmit: ({ value }) => {20 // Handle form submission here21 },22 });23 24 return (25 <form26 onSubmit={(event) => {27 event.preventDefault();28 event.stopPropagation();29 void form.handleSubmit();30 }}31 >32 <form.Field name="region">33 {(field) => (34 <Field.Item name={field.name}>35 <Field.Label>Region</Field.Label>36 <Combobox.Root37 value={field.state.value}38 setValue={(value) => {39 field.handleChange(value);40 }}41 >42 <Field.Control>43 <Combobox.Input onBlur={field.handleBlur} placeholder="Choose a region..." />44 </Field.Control>45 <Combobox.Content>46 <Combobox.Item value="us-east-1">US East</Combobox.Item>47 <Combobox.Item value="us-west-2">US West</Combobox.Item>48 <Combobox.Item value="eu-west-1">EU West</Combobox.Item>49 </Combobox.Content>50 </Combobox.Root>51 <Field.Errors messages={toErrorMessages(field.state.meta.errors)} />52 </Field.Item>53 )}54 </form.Field>55 <Button type="submit" appearance="filled">56 Submit57 </Button>58 </form>59 );60}Compose the parts of a Combobox together to build your own:
Combobox.Root├── Combobox.Input└── Combobox.Content ├── Combobox.Group │ ├── Combobox.GroupLabel │ └── Combobox.Item │ └── Combobox.ItemValue └── Combobox.SeparatorThe Combobox components are built on top of ariakit Combobox.
Root component for a combobox. Provides a combobox store that controls the state of Combobox components.
All props from ariakit ComboboxProvider.
Renders a combobox input element that can be used to filter a list of items.
All props from ariakit Combobox, plus:
Renders a popover that contains combobox content, e.g. items, groups, and separators.
All props from ariakit ComboboxPopover, plus:
Renders a combobox item inside a Combobox.Content component.
All props from ariakit ComboboxItem, plus:
Highlights the match between the current Combobox.Input value and parent Combobox.Item value. Should only be used as a child of Combobox.Item.
All props from ariakit ComboboxItemValue, plus:
Renders a group for Combobox.Item elements. Optionally, a Combobox.GroupLabel can be rendered as a child to provide a label for the group.
All props from ariakit ComboboxGroup, plus:
Renders a label in a combobox group. Should be wrapped with Combobox.Group so the aria-labelledby is correctly set on the group element.
All props from ariakit ComboboxGroupLabel, plus:
Renders a separator between Combobox.Items or Combobox.Groups.
All props from Separator.