Components

Tag input

Free-text-first sibling of the multi Combobox. The user types arbitrary strings (IPs, webhook URLs, allowed origins, custom tags) and each becomes a removable chip when they hit a delimiter.

Updated Jul 30, 2026 by Leonardo Posada

Anatomy

The container matches the Input field visual — same border, same focus ring. Inside, three optional chrome pieces surround the inline text input.

192.158.1.38192.158.1.38
  1. 1
    Container

    h-9 min, border-input, rounded-md, no shadow, focus ring-[3px] ring-ring/50 — matches Input.

  2. 2
    Delete all pill

    Filled muted with font-semibold. Visible only when there is at least one tag.

  3. 3
    Tag chip

    Outline + bg-background + text-foreground, matching <Combobox multiple>. Bold X on the right removes the chip.

  4. 4
    +N overflow

    Same outline as a chip. Shown when the number of tags exceeds maxChips.

  5. 5
    Inline input

    Free-text field. Delimiter keys (Enter / , / ; / space) commit the pending value as a chip. Backspace on an empty input removes the last chip.

Recipes

Canonical compositions. Copy the snippet, adjust props, ship it.

Basic — IPs

Free-text IP list using the default delimiters (Enter / , / ; / space). Try typing 10.0.0.1 then Enter.

192.158.1.38
<TagInput defaultValue={["192.158.1.38"]} placeholder="Enter IP address" />
With validation

A candidate is silently rejected when validate returns false. Try typing not-an-ip and pressing Enter — nothing happens.

<TagInput
  placeholder="Enter IP address"
  validate={(v) => /^\d{1,3}(\.\d{1,3}){3}$/.test(v)}
/>
Overflow chips

Collapse tags past maxChips into a +N pill. Useful in tight rows (e.g. inside a table cell).

abc+2
<TagInput
  defaultValue={["a", "b", "c", "d", "e"]}
  maxChips={3}
/>
Enter-only

For emails or paths where commas / spaces are meaningful. Only Enter commits a tag.

<TagInput placeholder="Enter email" delimiters={["Enter"]} />

Import

import { TagInput } from "@/components/ui/tag-input";

Props

PropTypeDefaultDescription
valuestring[]Controlled tags list.
defaultValuestring[]Uncontrolled initial tags.
onValueChange(tags: string[]) => voidFires on any change (add, remove, clear).
placeholderstring"Enter a value"Placeholder shown in the inline input when the field is empty.
delimitersstring[]["Enter", ",", ";", " "]Keys that commit the pending input as a tag.
clearablebooleantrueShow the 'Delete all' pill when there are tags.
validate(candidate: string) => booleanReturn false to silently reject a candidate.
format(candidate: string) => stringFormatter applied before validation (lowercase, strip, etc.). Trim is always applied first.
deduplicatebooleantrueReject duplicates.
maxChipsnumberMax chips visible before collapsing extras into +N.
maxTagsnumberHard cap on total number of tags.
disabledbooleanDisable the whole field.

When to use

  • User needs to enter multiple free-text values into a single field.
  • Values are validated per-item (IP, URL, email, hostname, custom string).
  • You want the chip affordance + Delete all shortcut but the list is open-ended.

When not to use

  • User picks from a fixed list of options — use <Combobox multiple>.
  • Single free-text value — use <Input>.
  • Long paragraphs — use <Textarea>.

Related

  • Combobox — the closed-list sibling. Same chip chrome, different interaction.
  • Input — the single free-text field this component composes over.
  • Badge — reference for the chip visual, though TagInput ships its own chip.