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
Props
Props declared by TagInput. Native attributes of the underlying element pass through.
| Prop | Type | Default | Description |
|---|---|---|---|
size | "sm" | "md" | "lg" | ||
value | string[] | Controlled tag list. | |
defaultValue | string[] | Uncontrolled initial tag list. | |
onChange | (tags: string[]) => void | Fires with the full accepted tag set after every add/remove. | |
label | react.ReactNode | Visible field label, associated to the text input. | |
helperText | react.ReactNode | Hint shown below the field. Hidden while an error message is showing. | |
errorMessage | react.ReactNode | Message shown below the field when invalid. | |
error | boolean | Marks the field invalid: sets aria-invalid and error styling. | |
max | number | Caps the number of tags. Adds beyond the cap are no-ops. | |
allowDuplicates | boolean | Allow the same tag more than once. Off → duplicates are rejected. | |
caseSensitive | boolean | Make duplicate detection case-sensitive. Default: case-insensitive. | |
validate | (tag: string, tags: string[]) => boolean | Consumer gate run on each candidate tag. Return false to reject (draft kept). | |
delimiters | string[] | 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) => string | Accessible name for each chip's remove button. | |
formatAnnouncement | (tag: string, action: "add" | "remove") => string | Live-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.