TagInput

React tag input that commits typed text as removable chips, with validation, a maximum count, de-duplication, and field wiring. Keyboard and Arabic ready.

Enter or a comma commits the text as a chip; Backspace removes the last one. It de-duplicates and supports max and validate.

When to use

Use TagInput to collect several short free-form values: email recipients, keywords, or reference numbers. Use validate to reject malformed entries and max to cap the count.

When the values come from a fixed list, use Dropdown with multiple so users pick rather than type.

Examples

Controlled tags

Capped at three

Validated email recipients

Arabic labels

Props

Props declared by TagInput. Native attributes of the underlying element pass through.

PropTypeDefaultDescription
size"sm" | "md" | "lg"
valuestring[]Controlled tag list.
defaultValuestring[]Uncontrolled initial tag list.
onChange(tags: string[]) => voidFires with the full accepted tag set after every add/remove.
labelreact.ReactNodeVisible field label, associated to the text input.
helperTextreact.ReactNodeHint shown below the field. Hidden while an error message is showing.
errorMessagereact.ReactNodeMessage shown below the field when invalid.
errorbooleanMarks the field invalid: sets aria-invalid and error styling.
maxnumberCaps the number of tags. Adds beyond the cap are no-ops.
allowDuplicatesbooleanAllow the same tag more than once. Off → duplicates are rejected.
caseSensitivebooleanMake duplicate detection case-sensitive. Default: case-insensitive.
validate(tag: string, tags: string[]) => booleanConsumer gate run on each candidate tag. Return false to reject (draft kept).
delimitersstring[]Characters that commit the draft and split a paste. Newline always splits a paste.
chipVariant"primary" | "secondary" | "outline" | "destructive" | "destructive-subtle" | "default" | "success" | "warning" | "info" | "primary-subtle" | "secondary-subtle" | "success-subtle" | "warning-subtle" | "info-subtle" | "on-color"Tag variant used for the chips.
removeLabel(tag: string) => stringAccessible name for each chip's remove button.
formatAnnouncement(tag: string, action: "add" | "remove") => stringLive-region text announced on add/remove (screen readers).

Accessibility

The chips render as a list, so screen readers hear how many values exist and can move through them. Enter or a comma commits a value and Backspace removes the last one, and a live region announces additions and removals.

The field label, helper text, and error are wired like TextInput. Each remove button carries the tag name in its accessible label.