InputTags
An unstyled tag input component with keyboard navigation, delimiter support, and optional typeahead suggestions.
Build fully custom tag input fields with complete control over layout and styling.
Pre-styled Versions
Features#
- Tag creation on Enter key press with optional delimiter character
- Maximum tag limit with
maxprop - Duplicate prevention with
allowDuplicate - Optional typeahead suggestions by composing
AutoComplete.RootinsideInputTags.Control - Add and remove event callbacks for fine-grained control
- Paste support with
addOnPasteand automatic delimiter splitting
Usage#
import { InputTags } from 'primereact/inputtags';<InputTags.Root value={tags} onValueChange={(e) => setTags(e.value)}>
<InputTags.Items>
{({ item, remove }) => (
<span>
{item}
<button onClick={remove}>×</button>
</span>
)}
</InputTags.Items>
<InputTags.Control>{({ controlProps }) => <input {...controlProps} />}</InputTags.Control>
</InputTags.Root>InputTags.Items is a pure render-prop iterator: it invokes children once per tag with { item, index, remove, itemProps }. Spread itemProps onto the rendered chip element to wire ARIA roles and the data-selected focus marker. InputTags.Control calls children exactly once with { controlProps, add, remove, removeLast, removeAll, value, inputValue, focused }. add accepts either a single string or an array of strings.
With Typeahead#
For suggestion-driven tag entry, mount an <AutoComplete.Root> tree inside <InputTags.Control>. Spread controlProps onto <AutoComplete.Input> so the text-entry gets InputTags' Backspace-to-remove, delimiter-split, addOnBlur / addOnPaste, and focus handlers. On AutoComplete.onValueChange (option selection), call add from the render-prop arg to commit the tag. Attach a callback ref + state to <InputTags.Root> and pass that element to <AutoComplete.Positioner anchor={...}> so the suggestion popup sizes against the full chip-input field rather than just the typing area.
import { AutoComplete } from 'primereact/autocomplete';
import { InputTags } from 'primereact/inputtags';
const [rootEl, setRootEl] = React.useState<HTMLElement | null>(null);
<InputTags.Root ref={setRootEl} value={tags} onValueChange={(e) => setTags(e.value)}>
<InputTags.Items>
{({ item, remove, itemProps }) => (
<span {...itemProps}>
{item}
<button onClick={remove}>×</button>
</span>
)}
</InputTags.Items>
<InputTags.Control>
{({ controlProps, add }) => (
<AutoComplete.Root
options={filteredItems}
onComplete={onSearch}
onValueChange={(e) => {
if (typeof e.value === 'string') add(e.value);
}}
>
<AutoComplete.Input {...controlProps} />
<AutoComplete.Portal>
<AutoComplete.Positioner anchor={rootEl}>
<AutoComplete.Popup>
<AutoComplete.List />
<AutoComplete.Empty>No results found</AutoComplete.Empty>
</AutoComplete.Popup>
</AutoComplete.Positioner>
</AutoComplete.Portal>
</AutoComplete.Root>
)}
</InputTags.Control>
</InputTags.Root>;Behavior#
Polymorphic Root#
Use as on InputTags.Root to change the rendered HTML element.
<InputTags.Root as="section">...</InputTags.Root>Render Prop Architecture#
The component intentionally has no Item / Label / Remove / Input sub-parts. Consumers compose the per-tag JSX (e.g. Chip.*, Tag, custom) inside InputTags.Items, and the text-entry element (a plain <input>, <AutoComplete.Input>, or anything else) inside InputTags.Control.
Pass Through#
Some parts may not be visible in the preview depending on the component's current state.
/* Select a part to see its CSS selector for custom styling */API#
InputTagsRoot#
| Name | Type | Default |
|---|---|---|
ref | Ref<unknown> | — |
| The reference to the component instance. | ||
pIf | boolean | true |
| Whether the component should be rendered. | ||
style | CSSProperties | ((instance?: InputTagsRootInstance) => CSSProperties) | — |
| The style to apply to the component. | ||
className | string | ((instance?: InputTagsRootInstance) => string) | — |
| The class name to apply to the component. | ||
as | string | number | bigint | boolean | ComponentClass<any, any> | FunctionComponent<any> | ReactElement<unknown, string | JSXElementConstructor<any>> | Iterable<ReactNode, any, any> | ReactPortal | Promise<AwaitedReactNode> | — |
| The component type to render. | ||
asChild | boolean | false |
| Whether the component should be rendered as a child component. | ||
instance | InputTagsRootInstance | — |
| The instance to pass to the component. | ||
pt | SafeRecord<InputTagsRootPassThrough> | — |
| The pass-through props to pass to the component. | ||
ptOptions | PassThroughOptions | — |
| The pass-through options to pass to the component. | ||
unstyled | boolean | — |
| Whether the component should be rendered without classes. | ||
dt | unknown | — |
| The design token to use for the component. | ||
styles | StylesOptions<ComponentInstance> | — |
| The styles to use for the component. | ||
render | (instance: InputTagsRootInstance) => ReactNode | — |
| The render function to render the component with instance access. | ||
children | any | — |
| The children to render. Accepts `React.ReactNode` for static content or a render function `(instance: I) => React.ReactNode` for instance access. Typed as `any` to avoid JSX type errors when used directly in templates. | ||
value | string[] | — |
| Controlled list of tag values. | ||
defaultValue | string[] | — |
| Initial tag values for uncontrolled usage. | ||
inputValue | string | — |
| Controlled value of the text-entry control. | ||
defaultInputValue | string | — |
| Initial value of the text-entry control for uncontrolled usage. | ||
max | number | — |
| Maximum number of tags that can be added. | ||
delimiter | string | RegExp | — |
| Delimiter used to split typed or pasted text into tags. | ||
allowDuplicate | boolean | — |
| Whether duplicate tag values are allowed. | ||
addOnBlur | boolean | — |
| Whether the current input value should be added when the control loses focus. | ||
addOnPaste | boolean | — |
| Whether pasted text should be added as tags. | ||
addOnTab | boolean | — |
| Whether the current input value should be added when tabbing away. | ||
onAdd | (event: useInputTagsAddEvent) => void | — |
| Callback fired after a tag is added. | ||
onRemove | (event: useInputTagsRemoveEvent) => void | — |
| Callback fired after a tag is removed. | ||
onInputValueChange | (event: useInputTagsInputValueChangeEvent) => void | — |
| Callback fired when the text-entry value changes. | ||
disabled | boolean | — |
| When present, it specifies that the component should be disabled. | ||
name | string | — |
| Name of the input element. | ||
invalid | boolean | — |
| When present, it specifies that the component should have invalid state style. | ||
variant | "outlined" | "filled" | — |
| Specifies the input variant of the component. | ||
fluid | boolean | — |
| When enabled, the component spans the full width of its parent. | ||
onValueChange | (event: InputTagsRootValueChangeEvent) => void | — |
| Callback fired when the inputtags's value changes. | ||
[key: string] | any | — |
pt-{optionName}-* | - | — |
| Pass through attributes for customizing component. For more info, see Pass Through tab. | ||
| Attribute | Value |
|---|---|
data-scope | "inputtags" |
data-part | "root" |
Defines passthrough(pt) options of InputTags component.
| label | type | description |
|---|---|---|
| root | InputTagsRootPassThroughType<HTMLAttributes<HTMLDivElement>> | Used to pass attributes to the root's DOM element. |
InputTagsItems#
InputTagsItems accepts a render-prop child invoked once per tag with { item, index, remove, itemProps }. Spread itemProps onto the rendered chip element for ARIA + keyboard navigation between tags.
InputTagsControl#
InputTagsControl accepts a render-prop child invoked once with { controlProps, add, remove, removeLast, removeAll, value, inputValue, focused }. Spread controlProps onto a text-entry element (plain <input>, <AutoComplete.Input>, etc.). Call add(tag) or add([tag1, tag2]) to commit one or more tags programmatically. When you need the root DOM node (for popup anchors, scroll-into-view, etc.), attach a callback ref via ref={setRootEl} on <InputTags.Root> and use the tracked state.
Accessibility#
Screen Reader#
The root container uses role="listbox" with aria-orientation="horizontal". Each tag (via itemProps) has role="option" with aria-selected, aria-setsize, and aria-posinset. The text-entry element's ARIA depends on what the consumer renders: a plain <input> is just a text field; an <AutoComplete.Input> adds combobox + aria-haspopup / aria-expanded / aria-controls semantics automatically.
Keyboard Support#
| Key | Function |
|---|---|
tab | Moves focus to the input element. |
enter | Adds a new tag with the current input value. |
backspace | Removes the last tag when input is empty. |
left arrow | Moves focus to the previous tag when input is empty. |
right arrow | Moves focus to the next tag when focused on a tag. |
Tag Keyboard Support#
| Key | Function |
|---|---|
backspace | Removes the focused tag. |
delete | Removes the focused tag. |