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

  • Enter commits the draft.
  • Paste with , splits, trims, and commits each part.
  • Whitespace trimmed; empties and duplicates dropped.
  • maxTags cap enforced on single adds and bulk paste.

Remove

  • Click the X on a tag.
  • Backspace on empty draft removes the last tag.
  • With a selection, Backspace / Delete removes all selected.
  • The X never steals focus from the input.

Select

  • Click a tag to toggle.
  • Shift+click for range; holding Shift while hovering previews the result — added tags ring soft, deselected fade to 50%.
  • Cmd/Ctrl+A (in empty input) selects all.
  • Escape clears the selection.
  • Focus leaving the component clears the selection.
  • Range semantics match the tasks list (pivot/head).

Undo / redo

  • Cmd/Ctrl+Z undoes; Cmd/Ctrl+Shift+Z or Cmd/Ctrl+Y redoes.
  • 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

PropTypeDefaultDescription
valuestring[]Current tags (controlled).
onValueChange(value: string[]) => voidCalled on every mutation.
placeholderstringAdd word or phrase and press enter...Input placeholder.
maxTagsnumber60Cap on tag count and counter denominator.
classNamestringMerged into the root container.