Fields
Tag Picker
An editable collection that turns chosen options into removable tags.
When to use it: Use it for multiple known choices such as recipients, skills, or filters.
Example
import {
ListBox,
Tag,
TagList,
TagPicker,
TagPickerInput,
TagPickerOption,
TextField,
} from "@comp0/react";
const frameworks = ["React", "Vue", "Svelte", "Solid"];
export function Example() {
return (
<TagPicker name="framework" defaultValue={["React"]} className="grid w-full max-w-sm gap-3">
{({ remove, value }) => (
<>
<TagList aria-label="Selected frameworks" className="flex flex-wrap gap-2">
{value.map((framework) => (
<Tag
key={framework}
value={framework}
className="flex items-center gap-1 rounded-full bg-teal-100 py-1 pl-3 pr-1 text-sm text-teal-950 data-focused:outline-2 data-focused:outline-teal-600 dark:bg-teal-950 dark:text-teal-50 dark:data-focused:outline-teal-400"
>
{framework}
<button
type="button"
aria-label={`Remove ${framework}`}
className="grid size-5 place-items-center rounded-full text-teal-800 hover:bg-teal-200 focus-visible:outline-2 focus-visible:outline-teal-600 dark:text-teal-100 dark:hover:bg-teal-900 dark:focus-visible:outline-teal-400"
onClick={() => remove(framework)}
>
×
</button>
</Tag>
))}
</TagList>
<TextField>
<TagPickerInput
aria-label="Add a framework"
placeholder="Find a framework"
className="w-full rounded border border-zinc-950/15 bg-transparent px-3 py-2 text-sm outline-teal-600 focus-visible:outline-2 dark:border-white/15 dark:outline-teal-400"
/>
</TextField>
<ListBox
aria-label="Frameworks"
className="rounded border border-zinc-950/10 p-1 dark:border-white/10"
>
{frameworks.map((framework) => (
<TagPickerOption
key={framework}
value={framework}
className="block cursor-pointer rounded px-3 py-2 text-sm data-active:bg-teal-50 data-focused:outline-2 data-focused:outline-teal-600 dark:data-active:bg-teal-950 dark:data-focused:outline-teal-400"
>
{framework}
</TagPickerOption>
))}
</ListBox>
</>
)}
</TagPicker>
);
}Anatomy
Dashed frames are invisible state providers; shaded shapes own real DOM. Numbered pins match the list below.
A wireframe sketch of the assembled component. Each numbered marker matches a part in the list that follows.
TagPicker
State provider that composes selected tags, an editable query, and available options. Does not add a DOM element.
TagList
Grid of selected Tag rows. Owns a DOM element.
Tag
Selected tag; may contain a pointer-reachable remove button. Owns a DOM element.
TextFieldOptional
Optional field wiring around the editable input. Does not add a DOM element.
TagPickerInput
Native text input for the option query. Owns a DOM element.
ListBox
Available matching options. Owns a DOM element.
TagPickerOption
Available option; selected values are omitted automatically. Owns a DOM element.
Step by step
- 1
Add the main part
Start TagPicker with a TagList that renders its value as Tag children.
- 2
Add the supporting parts
Add a TagPickerInput inside TextField and offer TagPickerOption children in a ListBox.
- 3
Make the behavior clear
Pass name when every selected value should submit; selecting an option clears the input and removes that option from the list.
Exampletsx <TagPicker name="framework"> {({ value }) => ( <> <TagList aria-label="Selected frameworks"> {value.map((value) => ( <Tag key={value} value={value}> {value} </Tag> ))} </TagList> <TextField> <TagPickerInput aria-label="Add framework" /> </TextField> <ListBox aria-label="Frameworks"> <TagPickerOption value="react">React</TagPickerOption> </ListBox> </> )} </TagPicker>;
Keyboard
- ↓↑
- Moves through matching options from the input.
- ↵
- Adds the active option.
- ⌫←
- Moves from an empty input to the last tag.
- ⌫
- Removes the focused tag; Delete does the same.
Forms and accessibility
TagPicker submits one hidden input per selected value under name and restores uncontrolled values on form reset.
Accessibility checklist
- Give both the selected TagList and available ListBox clear names when visible labels do not supply them.
- Backspace or ArrowLeft from an empty input moves to the last tag; Delete and Backspace remove a focused tag, so keep the tag text meaningful.
- Do not make color the only difference between selected and available options.
API reference
import { ListBox, Tag, TagList, TagPicker, TagPickerInput, TagPickerOption, TextField } from "@comp0/react";TagPicker
Context onlyState provider that composes selected tags, an editable query, and available options.
| Prop | Type | Description |
|---|---|---|
value | string[] | Controlled unique tag values. |
defaultValue | string[] | Initial unique tag values. |
onChange | (value: string[]) => void | Receives the next selected values. |
inputValue | string | Controlled query text. |
defaultInputValue | string | Initial query text. |
onInputChange | (value: string) => void | Receives the next query text. |
name | string | Submits one hidden input per selected value. |
filter | AutocompleteProps["filter"] | Optional match rule for available options. |
disabled | boolean | Disables adding, removing, and submitted values. |
TagList
DOM elementGrid of selected Tag rows.
Tag
DOM elementSelected tag; may contain a pointer-reachable remove button.
| Prop | Type | Description |
|---|---|---|
value | string | Selected value removed by the picker state. |
TextField
OptionalContext onlyOptional field wiring around the editable input.
TagPickerInput
DOM elementNative text input for the option query.
ListBox
DOM elementAvailable matching options.
TagPickerOption
DOM elementAvailable option; selected values are omitted automatically.
| Prop | Type | Description |
|---|---|---|
value | string | Unique value to add to the tag list. |
Style hooks
Attributes that appear while a state is true. Target them with Tailwind data variants such as data-open:bg-zinc-100, or with any CSS selector.
TagPicker
| Style hook | Meaning |
|---|---|
[data-empty] | No values are selected. |
[data-disabled] | The picker is disabled. |
TagPickerOption
| Style hook | Meaning |
|---|---|
[data-active] | The option has the keyboard highlight. |