Forms
Tag input
A list of tags with a text field on the end.
Import
import { TagInput } from '@the_viveksingh/vivek-ui'A list of tags
Enter or a delimiter commits a tag; Backspace in an empty box removes the last one. Each chip has its own labelled remove button, so a keyboard user is not stuck.
<TagInput
defaultValue={['react', 'typescript', 'accessibility']}
placeholder="Add a topic"
aria-label="Topics"
onChange={setTopics}
/>Validated
validate returns true, false, or a message shown to the user. Returning the reason is what turns a silent rejection into something fixable. Being a function, it also means the surrounding component has to be a Client Component.
Type an address and press Enter or comma.
<Field label="Invite by email" help="Type an address and press Enter or comma.">
<TagInput
max={5}
delimiters={[',', ' ']}
addOnBlur
validate={(tag) => (tag.includes('@') ? true : 'That is not an email address')}
placeholder="name@company.com"
/>
</Field>Props
Generated from the package's own type declarations, so this table cannot drift from the code.
| Prop | Type | Default | Description |
|---|---|---|---|
value | string[] | — | Controlled tags. |
defaultValue | string[] | — | Uncontrolled initial tags. Default `[]`. |
onChange | (value: string[]) => void | — | Called with the whole new list on every add or remove, in both modes. |
max | number | — | Hard cap. Further tags are rejected with reason `'max'`. |
validate | (tag: string, tags: string[]) => boolean | string | — | Vet a candidate. Return `false` to reject silently, or a string to reject *and* show that message — which is the version worth using, because "nothing happened" is the least debuggable failure a form can offer. |
allowDuplicates | boolean | — | Permit the same tag twice. Default `false`. |
placeholder | string | — | — |
size | 'sm' | 'md' | 'lg' | — | — |
disabled | boolean | — | — |
readOnly | boolean | — | — |
invalid | boolean | — | Sets `aria-invalid` on the text input. Injected by `Field`. |
required | boolean | — | Injected by `Field`. Marks the input required while there are no tags. |
name | string | — | Submits one hidden input per tag, all under this name. |
delimiters | string[] | — | Extra characters that commit a tag. Default `[',']` — Enter always commits. |
addOnBlur | boolean | — | Commit whatever is typed when the field loses focus. Default `true`. |
removeLabel | (tag: string) => string | — | Accessible name for a tag's remove button. Default `` `Remove ${tag}` ``. |
onReject | (tag: string, reason: TagRejectReason) => void | — | Told about every refusal, with the reason. |
Every remaining prop is spread onto the root element, so all standard HTML and ARIA attributes work. className and style are merged with the library's own, never replaced, and the ref forwards to the root DOM node.
Rendering
TagInput declares 'use client' because it needs state, effects or event handlers. Importing it into a Server Component creates a client boundary at this component — everything above it stays on the server.