Skip to content
WANCRAFT

F.52Forms & Inputs

Tag Input

A chips input with paste splitting, duplicate shake, keyboard chip navigation and accessible suggestions.

NewReactMotionTailwindadvanced

Overview

Tag Input turns typed text into chips. Enter, comma or Tab adds a tag; Backspace on an empty field selects the last chip and a second press removes it; arrow keys move between chips, and Delete or Backspace removes the focused one. Pasted lists are split on commas and new lines, duplicates are rejected with a shake and a highlight, and an optional maxTags limit is enforced and announced. Suggestions use the ARIA combobox pattern (listbox + aria-activedescendant), chips spring in and out with shared layout, and a hidden input submits the joined value with native forms. Controlled or uncontrolled.

01Installation

Method 01 · Registry

CLI verified

Uses the shadcn CLI in a project initialised with shadcn init. Installs the file to components/ui/tag-input.tsx and adds its dependencies.

npx shadcn@latest add https://wancraft.my/r/tag-input.json

Method 02 · Copy and paste

  1. 1. Install the dependencies below.
  2. 2. Copy the source from the Code tab into components/ui/tag-input.tsx.
  3. 3. Import it and adjust props.
npm install motion
Dependencies
motion
Peer
react ≥ 18, tailwindcss ≥ 4
Added
13 Oct 2026
Updated
—

02Usage

The default configuration. Change properties above to generate a tailored example in the Usage tab.

example.tsx
import { TagInput } from "@/components/ui/tag-input";

export function TagInputExample() {
  const suggestions = ["React", "TypeScript", "Motion", "Tailwind", "Next.js", "Node"];

  return (
    <div className="w-full max-w-md">
      <TagInput
        label="Skills"
        name="skills"
        defaultValue={["React"]}
        suggestions={suggestions}
      />
    </div>
  );
}

03Props

PropTypeDefaultDescription
value / defaultValuestring[]—Controlled tags, or initial tags when uncontrolled.
onChange(tags: string[]) => void—Called with the full list after every add or remove.
labelReactNode"Tags"Visible <label> for the text input.
hintReactNode—Helper text, linked via aria-describedby.
errorReactNode—Truthy marks the field invalid; a node/string is shown and announced.
placeholderstring"Add a tag…"Shown while there are no tags.
maxTagsnumber8Maximum number of tags; 0 means unlimited.
suggestionsstring[]—Optional suggestions shown in a combobox listbox; already-added entries are hidden.
caseSensitivebooleanfalseTreat differently-cased tags as distinct when checking duplicates.
addOnBlurbooleanfalseCommit pending text when focus leaves the field.
accentstring"#FF7448"Focus ring, caret and chip highlight colour.
disabledbooleanfalseDisables input and remove buttons.
name / delimiterstring— / ","Hidden input name and the separator used to join its value.
inputIdstring—id for the text input (generated when omitted).
...propsHTMLAttributes<HTMLDivElement>—Native attributes for the wrapper; aria-describedby is forwarded to the input.

04Features

  • 01Enter, comma or Tab adds; Backspace on an empty field selects, then removes, the last chip
  • 02Arrow keys, Home and End move between chips; Delete or Backspace removes the focused chip
  • 03Paste splits on commas and new lines; duplicates shake and highlight the existing chip
  • 04maxTags limit with a live counter, placeholder change and a field shake
  • 05Optional suggestions with substring highlighting and mouse or keyboard selection
  • 06Chips animate in and out with AnimatePresence and layout; controlled or uncontrolled with a hidden form value
motion · springmotion · layoutinput · keyboardinput · focusinput · click

05Accessibility

  • Real <label htmlFor> on the text input; hint, error and a usage description are joined in aria-describedby.
  • With suggestions the input is a combobox (aria-expanded, aria-controls, aria-autocomplete="list") that tracks the highlighted option with aria-activedescendant.
  • Chips form a list; each has a real remove button named "Remove <tag>", reachable with the arrow keys so the field stays a single tab stop.
  • Additions, removals, duplicates and the limit are announced in a polite live region; errors set aria-invalid.
  • prefers-reduced-motion swaps springs and shakes for instant fades; duplicates are still shown with a highlight.

06Limitations

  • The suggestion list is filtered on the client by substring; for remote search pass already-filtered suggestions.