Skip to content

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.

On this page

Example

React
Vue
Svelte
Solid
Tag Picker.tsxtsx
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.

  1. TagPicker

    State provider that composes selected tags, an editable query, and available options. Does not add a DOM element.

  2. TagList

    Grid of selected Tag rows. Owns a DOM element.

  3. Tag

    Selected tag; may contain a pointer-reachable remove button. Owns a DOM element.

  4. TextFieldOptional

    Optional field wiring around the editable input. Does not add a DOM element.

  5. TagPickerInput

    Native text input for the option query. Owns a DOM element.

  6. ListBox

    Available matching options. Owns a DOM element.

  7. TagPickerOption

    Available option; selected values are omitted automatically. Owns a DOM element.

Step by step

  1. 1

    Add the main part

    Start TagPicker with a TagList that renders its value as Tag children.

  2. 2

    Add the supporting parts

    Add a TagPickerInput inside TextField and offer TagPickerOption children in a ListBox.

  3. 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

Importtsx
import { ListBox, Tag, TagList, TagPicker, TagPickerInput, TagPickerOption, TextField } from "@comp0/react";

TagPicker

Context only

State provider that composes selected tags, an editable query, and available options.

PropTypeDescription
valuestring[]Controlled unique tag values.
defaultValuestring[]Initial unique tag values.
onChange(value: string[]) => voidReceives the next selected values.
inputValuestringControlled query text.
defaultInputValuestringInitial query text.
onInputChange(value: string) => voidReceives the next query text.
namestringSubmits one hidden input per selected value.
filterAutocompleteProps["filter"]Optional match rule for available options.
disabledbooleanDisables adding, removing, and submitted values.

TagList

DOM element

Grid of selected Tag rows.

Tag

DOM element

Selected tag; may contain a pointer-reachable remove button.

PropTypeDescription
valuestringSelected value removed by the picker state.

TextField

OptionalContext only

Optional field wiring around the editable input.

TagPickerInput

DOM element

Native text input for the option query.

ListBox

DOM element

Available matching options.

TagPickerOption

DOM element

Available option; selected values are omitted automatically.

PropTypeDescription
valuestringUnique 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 hookMeaning
[data-empty]No values are selected.
[data-disabled]The picker is disabled.

TagPickerOption

Style hookMeaning
[data-active]The option has the keyboard highlight.

Keep exploring