Tag Input
Usage
Jamie
Notetaker
2/60
'use client'
import { TagInput } from '@jamie/ui/tag-input'
import { useState } from 'react'
export function TagInputDemo() {
const [tags, setTags] = useState<string[]>(['Jamie', 'Notetaker'])
return <TagInput value={tags} onValueChange={setTags} className="w-[560px]" />
}import { TagInput } from '@jamie/ui/tag-input'
const [tags, setTags] = useState<string[]>([])
<TagInput value={tags} onValueChange={setTags} />Add
Entercommits the draft.- Paste with
,splits, trims, and commits each part. - Whitespace trimmed; empties and duplicates dropped.
maxTagscap enforced on single adds and bulk paste.
Remove
- Click the X on a tag.
Backspaceon empty draft removes the last tag.- With a selection,
Backspace/Deleteremoves all selected. - The X never steals focus from the input.
Select
- Click a tag to toggle.
- Shift+click for range; holding
Shiftwhile hovering previews the result — added tags ring soft, deselected fade to 50%. - Cmd/Ctrl+A (in empty input) selects all.
Escapeclears the selection.- Focus leaving the component clears the selection.
- Range semantics match the tasks list (pivot/head).
Undo / redo
Cmd/Ctrl+Zundoes;Cmd/Ctrl+Shift+ZorCmd/Ctrl+Yredoes.- Only fires when the input is empty and untouched since the last commit — otherwise the browser's text-edit history wins.
- One commit, paste, or removal = one step.
- Redo stack clears on any new commit.
Motion
- Tag enter/exit fade + scale.
- Container reflows with a shared spring on add/remove.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
value | string[] | — | Current tags (controlled). |
onValueChange | (value: string[]) => void | — | Called on every mutation. |
placeholder | string | Add word or phrase and press enter... | Input placeholder. |
maxTags | number | 60 | Cap on tag count and counter denominator. |
className | string | — | Merged into the root container. |