@mshafiqyajid/react-tag-input
Headless tag input hook and styled component. Chip-style tags, autocomplete dropdown, keyboard navigation, validation, max tags.
Playground #
ReactTypeScript
Paste multiple tags split by comma / newline / tab / semicolon.Props
TSX
import { TagInputStyled } from "@mshafiqyajid/react-tag-input/styled";
import "@mshafiqyajid/react-tag-input/styles.css";
<TagInputStyled
value={tags}
onChange={setTags}
/>Install #
npm install @mshafiqyajid/react-tag-input Quick start #
import { TagInputStyled } from "@mshafiqyajid/react-tag-input/styled";
import "@mshafiqyajid/react-tag-input/styles.css";
const [tags, setTags] = useState<string[]>([]);
<TagInputStyled
value={tags}
onChange={setTags}
suggestions={["React", "TypeScript", "Node.js"]}
maxTags={5}
label="Technologies"
tone="primary"
/> Headless #
import { useTagInput } from "@mshafiqyajid/react-tag-input";
const { tags, inputProps, removeTag } = useTagInput({
defaultValue: ["react", "ts"],
maxTags: 5,
});
return (
<div className="tags">
{tags.map((tag, i) => (
<span key={i} className="tag">
{tag}
<button onClick={() => removeTag(i)}>×</button>
</span>
))}
<input {...inputProps} placeholder="Add tag..." />
</div>
); API #
| Prop | Type | Default | Description |
|---|---|---|---|
| value | string[] | — | Controlled tags array |
| defaultValue | string[] | [] | Uncontrolled initial tags |
| onChange | (tags) => void | — | Called when tags change |
| suggestions | string[] | [] | Autocomplete suggestions |
| maxTags | number | — | Maximum number of tags |
| allowDuplicates | boolean | false | Allow duplicate tags |
| delimiter | string | string[] | ["Enter", ","] | Keys that confirm a tag |
| validate | (tag) => boolean | — | Custom validation function |
| tagVariant | "solid" | "subtle" | "outline" | "solid" | Tag chip style |
| label | string | — | Visible label |
| hint | string | — | Helper text below input |
| error | string | — | Error message (overrides hint) |
| size | "sm" | "md" | "lg" | "md" | Input size |
| tone | "neutral" | "primary" | "success" | "danger" | "neutral" | Color tone |
| disabled | boolean | false | Disable interaction |
| maxLength | number | — | Max chars per tag |
| caseSensitive | boolean | false | Case-sensitive duplicate detection |
| onTagAdd | (tag) => void | — | Tag added callback |
| onTagRemove | (tag, index) => void | — | Tag removed callback |
| renderTag | (tag, i, onRemove) => ReactNode | — | Custom chip renderer |
| sortable | boolean | false | Drag-to-reorder tags |
| pasteDelimiters | (string | RegExp)[] | null | [",", ";", /\s*\n\s*/, /\s*\t\s*/] | Split pasted text into multiple tags. Pass null to disable. |
| transform | (tag) => string | — | Normalize each tag before insertion (e.g. lowercase, trim). |
| backspaceEditsLastTag | boolean | false | When the input is empty, Backspace pops the last tag back into the input for editing instead of deleting it. |