Registry Index

Search the catalog

Type to search items across every indexed registry, or pick a page.

ComponentReactregistry:ui1 dependency

Tag Input

tag-input

A multi-value text field: what gets typed becomes a removable chip, and the value the form sees is a plain string[]. Enter or a comma commits the draft, Backspace on an empty field takes back the last chip, the x on a chip removes it, and pasting a comma- or newline-separated list adds the whole list at once. Reach for it wherever a field takes several short values a person makes up as they go: the tags, labels, topics or keywords row of a create or edit form, the To/Cc/Bcc recipients of a compose box, an invite-by-email box that takes a pasted column out of a spreadsheet, a filter bar accepting several terms at once, skills or interests on a profile, SEO keywords or meta tags in a CMS, an allowlist of domains, IP ranges, origins or redirect URIs in a settings panel, environment-variable keys, and the categories on a product or article. Common asks it answers: "tag input", "tags input react", "chips input", "token input", "multi value input", "add tags with Enter", "comma separated tags field", "type and press enter to add", "email recipient input", "invite emails input", "bcc chips field", "keyword filter input", "allowlist input", "react-tag-input alternative", "react-tagsinput alternative", "tagify alternative", "react-select creatable alternative", "shadcn tags field", "shadcn chips input". shadcn/ui has no tag, chip or token input anywhere in its sixty-odd components, so this is one an agent writes inline — and the inline version has three bugs that all look fine on the screen where you test them. The first is the form: a keydown handler that adds a tag on Enter without calling preventDefault leaves the Enter to do its normal job, so pressing it inside a <form> submits a half-filled form instead of adding a tag (and the comma, unstopped, is also typed into the field). Both keys are prevented here, and your own onKeyDown still runs first and can preventDefault to take the key back. The second is paste, which is where multi-value fields actually get used and where the obvious loop is wrong: calling addTag once per pasted value reads the tag list from a render that has not happened yet, so every iteration after the first sees a stale list — the cap and the duplicate check are computed against it, and depending on how state is set, most of the pasted values are silently dropped. The add is written as a pure function over a working list instead, threaded through every candidate and committed once, so ten pasted emails arrive as ten chips with one onChange and one announcement. The third is that a chip appearing or vanishing is a change nobody using a screen reader hears — the input's own value did not change, and neither did focus — so each add and remove is spoken through a polite live region ("Added design", "Removed design", "Added 5 tags" for a batch), and every chip's remove button carries its own label naming the tag it drops rather than a row of identical "Remove" buttons. Duplicates are matched case-insensitively, so React and react are one tag and not two, and allowDuplicates turns that off. The remove buttons are deliberately outside the tab order: a field holding twenty tags would otherwise be twenty-one tab stops on the way to the next input, so keyboard removal is Backspace from the field and the x is there for the pointer. Controlled with value/onChange or uncontrolled with defaultValue; max caps the count, validate rejects a candidate before it becomes a chip (an email regex, a lowercase-slug rule), the ref forwards to the real input so you can focus it, and clicking anywhere in the box focuses it too. The string[] drops straight into react-hook-form's Controller or any controlled state in React or Next.js, and remaining props land on the inner input, so placeholder, name, id, aria-* and data-* all work. Chips use the secondary token and the box uses input, ring and muted-foreground, so it follows light and dark with no extra styling; className styles the box and inputClassName the field inside it. One file, one lucide icon, no tag library. Distinct from pulld multi-select, which picks from a fixed list of options you supply: reach for that one when the set of valid values is known, and for this one when the person is inventing them.

Couldn't load the source right now — try refreshing.
3 other registries

Similar components

Other registries' take on tag-input.