Tag input
Turn short text values into removable tags.
import { TagInput } from "@sagui/ui";Loading demo
When to use
- Free-form lists of short values like tags, keywords, or emails.
- Fields where people add values by pressing Enter or comma.
When not to use
- Use multi-select or chip-group when values come from a fixed list.
- Use input for a single value.
Installation
Install the package and import the styles once. The installation guide covers the Tailwind setup.
$ npm install @sagui/uiUsage
import { TagInput } from "@sagui/ui";
export function TopicsField() {
const [topics, setTopics] = useState(["design", "motion"]);
return (
<TagInput
label="Topics"
value={topics}
onValueChange={setTopics}
description="Press Enter or comma to add."
/>
);
}Examples
Empty
Loading demo
Controlled
Tags are plain strings. Normalize or validate in onValueChange.
Loading demo
Many tags
The field grows with its wrapped rows on a spring.
Loading demo
API reference
TagInput
A field that turns typed text into removable tags, with keyboard picking and a gliding selection ring.
| Prop | Type | Default | Description |
|---|---|---|---|
label (required) |
string |
– | Visible label tied to the text input. |
value |
string[] |
– | Controlled tags. |
defaultValue |
string[] |
[] |
Initial tags when uncontrolled. |
onValueChange |
(value: string[]) => void |
– | Called with the full list after each add or remove. |
placeholder |
string |
"Add a tag" |
Shown while there are no tags. |
description |
string |
– | Helper copy under the field. |
error |
string |
– | Error copy. Colors the border, sets aria-invalid, and is announced as an alert. |
className |
string |
– | Added to the root. |
id |
string |
– | Input id. Generated when omitted. |
Keyboard interactions
| Keys | Action |
|---|---|
| Enteror, | Adds the typed text as a tag. Duplicates (case-insensitive) pulse the existing tag instead. |
| Backspace | At the start of the input, picks the last tag; pressed again, removes the picked tag. |
| Delete | Removes the picked tag. |
| ArrowLeftorArrowRight | Moves the pick between tags and back to the input. |
| Escape | Clears the pick. |
Accessibility
- Each tag has a remove button labelled "Remove ".
- Adds, removals, picks, and duplicates are announced in a polite aria-live region.
- Blurring the input commits any typed draft as a tag.
Motion
- New tags blur and scale in where the text was typed; removed tags leave and the rest glide on the morph spring. The shell follows wrapped rows on a smooth spring.
- A ring glides between picked tags. Reduced motion removes layout travel, scale, and blur, keeping fades.
Responsive behavior
- Tags wrap onto new rows and the shell height follows on a spring, so the field never scrolls horizontally.
- The text input keeps an 80px minimum, dropping to its own row when the last row is full.
Performance
- Every tag is a motion element with layout position animation; fine for dozens of tags, not hundreds.
- Two ResizeObservers track the content and message heights.
Notes
- Use for free-form values like tags, emails, or keywords. Use multi-select or chip-group when values come from a fixed list.
- Controlled or uncontrolled. Tags are plain strings; validate or normalize in onValueChange.