Tag input
Keywords entered through a field, as removable chips
Publish @acme/ui
reactgpuixcomponentsdesigndocs5 max
5 of 5 keywords added: the list is full. · refused=0
Ready to publish 5 keywords.
Installation
npx shadcn@latest add @sudokw/components-tag-inputimport { TagInput } from "@gpuixui/components";Usage
import {
TagInput,
TagInputChip,
TagInputField,
} from "@gpuixui/components/tag-input";
import { useState } from "react";
export const Keywords = () => {
const [keywords, setKeywords] = useState<ReadonlyArray<string>>(["design"]);
return (
<TagInput
label="Package keywords"
max={5}
hint="5 max"
caption={`${keywords.length} of 5 keywords`}
value={keywords}
onValueChange={setKeywords}
>
{keywords.map((keyword) => (
<TagInputChip key={keyword} value={keyword} />
))}
<TagInputField aria-label="Add a keyword" placeholder="Add a keyword…" />
</TagInput>
);
};Composition
TagInput
├── TagInputChip
└── TagInputFieldAPI reference
TagInput
| Prop | Type | Default | Consequence |
|---|---|---|---|
value | ReadonlyArray<string> | — | the tokens; the caller owns and renders them, and a stored value that is not a list carries no tokens |
onValueChange | (value: ReadonlyArray<string>) => void | — | the list after every accepted edit, including a removal |
label | string | — | the group's accessible name |
max | number | none | additions past it are refused; the chips stay |
allowDuplicates | boolean | false | a repeated token is accepted as its own chip |
disabled | boolean | false | refuses every edit path and renders no removal control |
hint | string | none | a quiet note at the end of the field; blank renders nothing |
caption | string | none | a caption under the field for the caller's preset or limit; blank renders nothing |
children | PartChildren | — | TagInputChip and TagInputField, directly inside |
className | string | — | joined after the outer recipe, so it wins conflicts |
style | StyleDesc | — | merged last over the recipe |
testId | string | — | stable locator for the group |
TagInputField
| Prop | Type | Default | Consequence |
|---|---|---|---|
placeholder | string | none | painted in the placeholder role while the draft is empty |
className | string | — | joined after the field recipe |
style | StateStyle<TagInputFieldState> | — | resolved against { disabled } and merged last |
testId | string | — | the input's locator; the painted hint follows it |
onKeyDown / onFocus / onBlur | host handlers | none | composed with the component's own; yours runs first |
label / description / invalid | string · string · boolean | none · none · false | the field's name, its announced help, and the invalid edge |
disabled / readOnly / autoFocus | boolean | false | inherited from the root's disabled, an uneditable field, and focus on mount |
size / fullWidth / tabIndex | "sm" | "md" | "lg" · boolean · number | "md" · false · the field's place | the input's height, its width, and its tab stop |
ref / aria-id / onLayout | the addressed element | none | the draft input: focus it, measure it, name it for automation |
onClick / onKeyUp | host handlers | none | composed with the field's own; yours runs first |
onValueChange / defaultValue | the field surface it inherits — see Input | none | the draft the field keeps before the tag is committed |
TagInputChip
| Prop | Type | Default | Consequence |
|---|---|---|---|
value | string | — | the token this chip removes; also its key |
className | string | — | joined after the chip recipe |
style | StateStyle<TagInputChipState> | — | resolved against { disabled } and merged last |
testId | string | none | the removal control is addressable as <testId>-remove |
Keyboard
- Enter
- commits the draft as a tag
- Space
- removes a chip from its remove target
read from tag-input.tsx
Edit this page on GitHubapps/docs/content/docs/components/tag-input.mdx