Portfolio

Tags Input

A versatile chip-style input with paste-splitting, dedupe, validation, max-tags, and full keyboard editing — the tags input shadcn doesn't ship.

Installation

bunx --bun shadcn@latest add https://ui.ahmet.studio/r/tags-input

Usage

import { TagsInput } from "@/components/ui/tags-input"; export function Demo() { const [tags, setTags] = React.useState<string[]>([]); return ( <TagsInput value={tags} onChange={setTags} placeholder="Add a tag..." maxTags={10} formatTag={(t) => t.toLowerCase().replace(/\s+/g, "-")} validate={(t) => /^[a-z0-9-]+$/i.test(t) || "Letters, numbers and dashes only" } /> ); }

Features

  • Paste-aware — pasting react, vue; svelte adds three tags at once.
  • Dedupe — duplicates are silently ignored (configurable).
  • Validation — return false or a string error from validate.
  • Max tags — disables the input when full.
  • Backspace edit — first backspace focuses the last chip; second removes it.
  • Submit on Enter, comma, or Tab — fully configurable via delimiters.
  • Format on commit — normalize values (slugify, lowercase, trim, etc.).
  • Sizessm, default, lg.
  • Controlled — fully controlled value / onChange.

Props

PropTypeDefault
valuestring[]Required
onChange(value: string[]) => voidRequired
placeholderstring"Add tag..."
delimitersstring[]["Enter", ","]
maxTagsnumberundefined
validate(tag: string) => boolean | stringundefined
duplicates"ignore" | "allow""ignore"
formatTag(raw: string) => stringundefined
clearablebooleantrue
pasteSplitRegExp/[\n,;]+/
size"sm" | "default" | "lg""default"

Keyboard

  • Enter / , / Tab — commit the current draft as a tag
  • Backspace (empty input) — focus the last chip; press again to remove
  • Esc — clear focused chip