Examples
Combobox with tags input

Combobox with TagsInput

Render a Combobox with tags that can be removed.

import { Combobox, createListCollection } from '@ark-ui/react/combobox'
import { TagsInput } from '@ark-ui/react/tags-input'
import { matchSorter } from 'match-sorter'
import { useId, useMemo, useRef, useState } from 'react'
import { data } from './data'

export const Example = () => {
  const [items, setItems] = useState(data)
  const [value, setValue] = useState<string[]>([])
  const [inputValue, setInputValue] = useState('')
  const contentRef = useRef<HTMLDivElement>(null)

  const ids = {
    root: useId(),
    input: useId(),
    control: useId(),
  }

  const collection = useMemo(
    () =>
      createListCollection({
        items,
      }),
    [items],
  )

  return (
    <Combobox.Root
      ids={ids}
      allowCustomValue
      multiple
      selectionBehavior="clear"
      collection={collection}
      value={value}
      onValueChange={(details) => {
        setValue(details.value)
        setItems((curr) => curr.filter((item) => !details.value.includes(item.value)))
        contentRef.current?.scrollTo(0, 0)
      }}
      onOpenChange={() => setItems(data.filter((item) => !value.includes(item.value)))}
      onInputValueChange={({ inputValue }) => {
        const result = matchSorter(data, inputValue, {
          keys: ['label'],
          baseSort: (a, b) => (a.index < b.index ? -1 : 1),
        })
        setItems(result)
      }}
      asChild
    >
      <TagsInput.Root
        ids={ids}
        value={value}
        inputValue={inputValue}
        editable={false}
        addOnPaste={false}
        onValueChange={(details) => setValue(details.value)}
        onInputValueChange={(details) => setInputValue(details.inputValue)}
      >
        <Combobox.Label>Country</Combobox.Label>
        <TagsInput.Context>
          {(tagsInput) => (
            <Combobox.Control asChild>
              <TagsInput.Control>
                {tagsInput.value.map((value, index) => (
                  <TagsInput.Item key={index} index={index} value={value}>
                    <TagsInput.ItemPreview>
                      <TagsInput.ItemText>
                        {data.find((item) => item.value === value)?.label ?? value}
                      </TagsInput.ItemText>
                      <TagsInput.ItemDeleteTrigger>Delete</TagsInput.ItemDeleteTrigger>
                    </TagsInput.ItemPreview>
                    <TagsInput.ItemInput />
                  </TagsInput.Item>
                ))}
                <Combobox.Input
                  asChild
                  placeholder="Add Country"
                  onKeyDown={(event) => {
                    if (event.key === 'Enter' && items.length === 0) {
                      tagsInput.addValue(inputValue)
                    }
                  }}
                >
                  <TagsInput.Input />
                </Combobox.Input>
              </TagsInput.Control>
            </Combobox.Control>
          )}
        </TagsInput.Context>
        <Combobox.Positioner>
          {items.length > 0 && (
            <Combobox.Content ref={contentRef}>
              {items.map((item) => (
                <Combobox.Item key={item.value} item={item}>
                  {item.label}
                </Combobox.Item>
              ))}
            </Combobox.Content>
          )}
        </Combobox.Positioner>
      </TagsInput.Root>
    </Combobox.Root>
  )
}