Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 87 additions & 5 deletions docs/src/app/(docs)/react/components/combobox/page.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -71,18 +71,98 @@ import { Combobox } from '@base-ui/react/combobox';
</Combobox.Root>;
```

## TypeScript
## Value modes

Combobox infers the item type from the `defaultValue` or `value` props passed to `<Combobox.Root>`.
The type of items held in the `items` array must also match the `value` prop type passed to `<Combobox.Item>`.
Combobox supports two value modes:

- **Item values**: `value` / `defaultValue` has the same shape as the entries in the `items` array.
- **Keyed values**: `value` / `defaultValue` is a primitive while `items` use the `{ value, label }` object shape.

In the keyed mode, the combobox uses `item.label` for display and filtering, and `item.value` as the selected value. Other object shapes such as `{ id, name }` are not supported by the keyed mode; use item-shaped values instead.

### Item values

Use object-shaped values when the selected value should keep the full item data instead of a primitive key.

- Use `itemToStringLabel` when the object does not already have a `label` field, or when you want to override the default `label` text shown in the input or trigger.
- Use `itemToStringValue` when form submission should serialize the object as a string, such as an `id`.
- Use `isItemEqualToValue` when items are re-created across renders or loaded asynchronously and object identity is not stable.

```tsx title="Object-shaped values" {13-15} "value={user}"
interface User {
id: number;
name: string;
}

const users: User[] = [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' },
];

<Combobox.Root
items={users}
itemToStringLabel={(user) => user.name}
itemToStringValue={(user) => String(user.id)}
isItemEqualToValue={(item, value) => item.id === value.id}
>
<Combobox.Input />
<Combobox.Portal>
<Combobox.Positioner>
<Combobox.Popup>
<Combobox.List>
{(user: User) => (
<Combobox.Item key={user.id} value={user}>
{user.name}
</Combobox.Item>
)}
</Combobox.List>
</Combobox.Popup>
</Combobox.Positioner>
</Combobox.Portal>
</Combobox.Root>;
```

### Keyed values

Use keyed values when `items` already have a `{ value, label }` shape and the selected value should be the primitive `item.value`.
Closed-trigger typeahead is only available once a primitive keyed value has already been selected.

```tsx title="Keyed values" "value"2,3,5 "defaultValue"
interface FruitItem {
value: string;
label: string;
}

const items: FruitItem[] = [
{ value: 'apple', label: 'Apple' },
{ value: 'banana', label: 'Banana' },
];

<Combobox.Root items={items} defaultValue="banana">
<Combobox.Input />
<Combobox.Portal>
<Combobox.Positioner>
<Combobox.Popup>
<Combobox.List>
{(item) => (
<Combobox.Item key={item.value} value={item.value}>
{item.label}
</Combobox.Item>
)}
</Combobox.List>
</Combobox.Popup>
</Combobox.Positioner>
</Combobox.Portal>
</Combobox.Root>;
```

## Examples

### Typed wrapper component

The following example shows a typed wrapper around the Combobox component with correct type inference and type safety:
Use a two-generic wrapper when the selected value has the same shape as the `items` entries:

```tsx title="Specifying generic type parameters"
```tsx title="Wrapper for item-shaped values"
import * as React from 'react';
import { Combobox } from '@base-ui/react/combobox';

Expand All @@ -93,6 +173,8 @@ export function MyCombobox<Value, Multiple extends boolean | undefined = false>(
}
```

If the wrapper should also support keyed `{ value, label }` items, forward the third `Items` generic as well.

### Multiple select

The combobox can allow multiple selections by adding the `multiple` prop to `<Combobox.Root>`.
Expand Down
8 changes: 4 additions & 4 deletions docs/src/app/(docs)/react/components/combobox/types.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@
| Prop | Type | Default | Description |
| :------------------- | :------------------------------------------------------------------------------------------------------ | :------ | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| name | `string` | - | Identifies the field when a form is submitted. |
| defaultValue | `Value[] \| Value \| null` | - | The uncontrolled selected value of the combobox when it's initially rendered. To render a controlled combobox, use the `value` prop instead. |
| value | `Value[] \| Value \| null` | - | The selected value of the combobox. Use when controlled. |
| defaultValue | `Value[] \| Value \| null` | - | The uncontrolled selected value of the combobox when it's initially rendered.&#xA;Must match the item value shape, except when `items` use the `{ value, label }` shape, in which case a primitive `item.value` is also supported. To render a controlled combobox, use the `value` prop instead. |
| value | `Value[] \| Value \| null` | - | The selected value of the combobox. Use when controlled.&#xA;Must match the item value shape, except when `items` use the `{ value, label }` shape, in which case a primitive `item.value` is also supported. |
| onValueChange | `((value: Value[] \| Value \| null, eventDetails: Combobox.Root.ChangeEventDetails) => void)` | - | Event handler called when the selected value of the combobox changes. |
| defaultInputValue | `string \| number \| string[]` | - | The uncontrolled input value when initially rendered. To render a controlled input, use the `inputValue` prop instead. |
| inputValue | `string \| string[] \| number` | - | The input value of the combobox. Use when controlled. |
Expand All @@ -33,9 +33,9 @@
| grid | `boolean` | `false` | Whether list items are presented in a grid layout.&#xA;When enabled, arrow keys navigate across rows and columns inferred from DOM rows. |
| inline | `boolean` | `false` | Whether the list is rendered inline without using the popup. |
| isItemEqualToValue | `((itemValue: Value, value: Value) => boolean)` | - | Custom comparison logic used to determine if a combobox item value matches the current selected value. Useful when item values are objects without matching referentially.&#xA;Defaults to `Object.is` comparison. |
| itemToStringLabel | `((itemValue: Value) => string)` | - | When the item values are objects (`<Combobox.Item value={object}>`), this function converts the object value to a string representation for display in the input.&#xA;If the shape of the object is `{ value, label }`, the label will be used automatically without needing to specify this prop. |
| itemToStringLabel | `((itemValue: Value) => string)` | - | When the item values are objects (`<Combobox.Item value={object}>`), this function converts the object value to a string representation for display in the input.&#xA;If the shape of the object is `{ value, label }`, the label will be used automatically for display and filtering without needing to specify this prop.&#xA;In that keyed mode, this prop is bypassed for the `{ value, label }` item array itself and only applies as a fallback when resolving other value shapes. |

Check warning on line 36 in docs/src/app/(docs)/react/components/combobox/types.md

View workflow job for this annotation

GitHub Actions / test-dev (ubuntu-latest)

[vale] reported by reviewdog 🐶 [Google.Will] Avoid using 'will'. Raw Output: {"message": "[Google.Will] Avoid using 'will'.", "location": {"path": "docs/src/app/(docs)/react/components/combobox/types.md", "range": {"start": {"line": 36, "column": 368}}}, "severity": "WARNING"}
| itemToStringValue | `((itemValue: Value) => string)` | - | When the item values are objects (`<Combobox.Item value={object}>`), this function converts the object value to a string representation for form submission.&#xA;If the shape of the object is `{ value, label }`, the value will be used automatically without needing to specify this prop. |
| items | `any[] \| Group[]` | - | The items to be displayed in the list.&#xA;Can be either a flat array of items or an array of groups with items. |
| items | `any[] \| Group[]` | - | The items to be displayed in the list.&#xA;Can be either a flat array of items or an array of groups with items.&#xA;Primitive `value`/`defaultValue` is only supported implicitly when each item has the shape `{ value, label }`. |
| limit | `number` | `-1` | The maximum number of items to display in the list. |
| locale | `Intl.LocalesArgument` | - | The locale to use for string comparison.&#xA;Defaults to the user's runtime locale. |
| loopFocus | `boolean` | `true` | Whether to loop keyboard focus back to the input when the end of the list is reached while using the arrow keys. The first item can then be reached by pressing ArrowDown again from the input, or the last item can be reached by pressing ArrowUp from the input.&#xA;The input is always included in the focus loop per [ARIA Authoring Practices](https://www.w3.org/WAI/ARIA/apg/patterns/combobox/).&#xA;When disabled, focus does not move when on the last element and the user presses ArrowDown, or when on the first element and the user presses ArrowUp. |
Expand Down
Loading
Loading