React Tag Input: Chips from Typing, Paste and Suggestions

A React tag input looks like a text field with chips in it, and the basic version is twenty lines: an array in state, a chip per item, add on Enter. Then people paste "emma@acme.com, liam@acme.com; olivia@acme.com" and get one long chip. They type a tag that already exists, hit a limit nobody explained, or tap a 12px × on a phone. This guide covers what a React tag input component has to handle, the props that matter in the Tag Input from Wingo UI (the library we build), accessibility, mobile behavior and the cases where a Multi Select or an Email Input is the better control.
What should a React tag input handle?
It should handle eight things: adding, paste, duplicates, validation, limits, editing, the keyboard and forms. This is the checklist we hold any React chips input to, ours included:
- Adding. Enter always adds. A comma or a semicolon adds too, and so does leaving the field, so typed text is never lost.
- Paste. A pasted list with commas, semicolons, tabs or new lines becomes one chip per entry, trimmed, duplicates once.
- Duplicates. A tag that already exists is not added again, compared without case or accents, and the existing chip shows where it is.
- Validation. A bad entry stays in the text field with a reason under it, instead of disappearing.
- Limits. A maximum number of tags and a maximum length per tag, both explained in the description.
- Editing. Backspace in the empty field brings the last chip back as text. A chip can be edited in place.
- Keyboard. Arrow keys move between chips, Delete removes the focused one, Escape returns to the text.
- Forms. The values reach
FormDataand react-hook-form without glue code.
The Tag Input does all of it. Install it and render it with suggestions:
A picked suggestion stores its value and the chip shows its label, so "nextjs" in your data reads "Next.js" on screen. Typing a label exactly also adds the value.
$ npx wingo-ui@latest add tag-inputWhich props matter most?
delimiters, max, maxLength, transform and validate cover most forms. The rest decide how duplicates, strict lists, Backspace, blur and paste behave:
transform and validate together cover most custom rules. Hashtags are a good example: lowercase the text, strip spaces, add the #, then check the result with the ready-made validator:
tagValidators also has email and url. With the email one the phone keyboard switches to the email layout. Every reason a tag is turned away ("invalid", "duplicate", "limit", "notInList") goes to onReject as well, in case you want to count them or show your own message.
How do I use a tag input with react-hook-form and zod?
Wrap it in Controller and validate the array with zod (our React multi step form tutorial shows the same pattern across several steps). register does not fit, because there is no single native input holding the value:
ref and onBlur land on the text input, so react-hook-form can focus the field when the array fails validation. Keep max in the component and the .max() rule in the schema: the first stops people early, the second protects the server.
Without react-hook-form, pass name and the Tag Input renders one hidden input per tag. A Server Action reads them with getAll:
Is the tag input accessible?
The text input keeps its label, and the box is a group named by that label. Each chip is focusable with a name that says what to do: "Tag bike, press Delete to remove", which labels.tag lets you change. Additions, removals, duplicates and the limit are announced through a polite live region, so a screen reader user hears "bike is already added" instead of nothing.
The keyboard model follows the text: Arrow Left at the start of the text moves onto the chips, Left and Right walk between them, Home and End jump, Delete or Backspace removes the focused chip and moves focus to its neighbor, Enter or F2 edits it in place, and Escape returns to the text. Suggestions open under the field with Arrow Down; Enter picks the highlighted one.
How does it behave on a phone?
Three details decide whether a tag input works on a phone:
- 16px text. iOS Safari zooms into inputs with smaller text. The Tag Input uses 16px on phones whatever the
size. - Delimiters read from the value. Android virtual keyboards usually fire
keydownwithkey: "Unidentified"(keyCode 229), so a handler that waits forevent.key === ","never sees the comma. The component checks the text inonChangeinstead, so a comma always splits. - Touch targets. The × on each chip has a hit area larger than the icon, and the suggestions stay in the page under the field, above the keyboard, instead of in a floating layer the keyboard can cover.
More on these rules in mobile-first React components.
When is a multi-select the better control?
Use a multi-select when the options are known and finite: assignees, categories, countries, permissions. People can scan the whole list, they cannot misspell anything, and your data never gets "Tailwind", "tailwind" and "tailwindcss" as three values.
The Multi Select, covered in depth in the React multi select guide, shows chips with a +N overflow, has a search row above eight options, selectAll, maxSelected, async loadOptions, and on a phone it opens a bottom sheet with 48px checkbox rows and a Done button:
$ npx wingo-ui@latest add multi-selectWhat about a list of email addresses?
Email recipients look like tags but need more: a name next to the address, contacts to pick from, and typo hints. The Email Input with multiple turns a pasted "Emma Carter emma@acme.com, liam@acme.com" into chips with names, keeps a bad address as a red chip to fix later (like a mail app does), and marks a likely typo such as gmial.com in amber with a one-tap fix:
$ npx wingo-ui@latest add email-inputHow does it compare with shadcn and other libraries?
shadcn/ui has no tag input, Mantine's TagsInput is the closest match feature for feature, and react-tag-input is the older standalone package with drag to reorder on by default. The details, as of October 2026 (Mantine v9.7 docs, react-tag-input README):
Sources: shadcn/ui component list (opens in a new tab) and Combobox docs (opens in a new tab), Mantine TagsInput (opens in a new tab), react-tag-input README (opens in a new tab).
If you need a shadcn tag input, the closest official piece is the Combobox in multiple mode, which is the right choice when values come from a list. Mantine's TagsInput is the better pick if your app already uses Mantine. If people must drag tags into an order, choose Mantine or react-tag-input: our Tag Input does not reorder. If you want a Tailwind tag input with paste splitting, validation with reasons, chip keyboard navigation and phone behavior already built, that is what the Wingo UI Tag Input covers.
Where should you start?
Start with the Tag Input docs: the playground and examples for emails, Backspace modes, a strict list, limits, chip styles and hashtags. Tag Input, Multi Select and Email Input are part of Wingo UI Pro: $8 a month, $80 a year or $150 once as of October 2026 (pricing). For the rest of a form, read the React form components guide, and find more guides in the component guides category.
FAQ
How do I create a tag input in React?
Keep an array of strings in state, render each one as a chip with a remove button, and add the trimmed text on Enter or a comma, skipping empty and duplicate values. A ready React tag input component such as Wingo UI's Tag Input adds paste splitting, suggestions, limits, validation and keyboard navigation between chips.
Does shadcn/ui have a tag input?
No. As of October 2026 the official shadcn/ui component list has no tag input. Its Combobox supports multiple selection with chips, which covers picking from a list, but its docs show no way to keep typed values that are not in the list, so those need your own code or a community component.
How do I handle pasting a list into a tag input?
Split the pasted text on commas, semicolons, tabs and new lines, trim each part, drop empty and duplicate entries, and validate each one before adding it. Wingo UI's Tag Input does this when addOnPaste is on, which is the default.
What is the difference between a tag input and a multi-select?
A tag input accepts values the user types, so the set of possible values is open. A multi-select picks from a fixed list of options with checkboxes, which is easier to scan and impossible to misspell.
How do I submit tags with a form in React?
Render one hidden input per tag with the same name and read them with formData.getAll(name), or wrap the tag input in react-hook-form's Controller and validate the array with zod. Wingo UI's Tag Input renders the hidden inputs when you pass name.
- Forms
- Tag Input
- Chips
- Accessibility
- Mobile UI


