Skip to content

TagsInput

Multi-select combobox that renders selected values as removable tags.

Ada Lovelace
<TagsInput
htmlName="team"
label="Team"
onChange={() => {}}
placeholder="Search people"
/>
function EmptyTagsInputStory(
args: React.ComponentProps<typeof TagsInput>,
): React.JSX.Element {
const [value, setValue] = useState<SearchableItem[]>([]);
const source = useMemo(() => createStaticSearchSource(people), []);
return (
<TagsInput
{...args}
debounceMs={0}
hasEntriesOnFocus
onChange={(items, change) => {
setValue(items);
args.onChange(items, change);
}}
searchSource={source}
startIcon={Users}
value={value}
/>
);
}
<EmptyTagsInputStory {...args} />
Ada Lovelace
<TagsInput
htmlName="team"
label="Team"
onChange={() => {}}
placeholder="Search people"
hasCreate
/>
Ada Lovelace
<TagsInput
htmlName="team"
label="Team"
onChange={() => {}}
placeholder="Search people"
hasClear
/>
function AsyncSearchStory(
args: React.ComponentProps<typeof TagsInput>,
): React.JSX.Element {
const [value, setValue] = useState<SearchableItem[]>([]);
const source = useMemo(() => createAsyncSource(people), []);
return (
<TagsInput
{...args}
debounceMs={0}
onChange={(items, change) => {
setValue(items);
args.onChange(items, change);
}}
searchSource={source}
startIcon={Users}
value={value}
/>
);
}
<AsyncSearchStory {...args} />
Ada Lovelace
<TagsInput
htmlName="team"
label="Team"
onChange={() => {}}
placeholder="Search people"
maxEntries={2}
/>
Ada Lovelace
<TagsInput
htmlName="team"
label="Team"
onChange={() => {}}
placeholder="Search people"
isDisabled
/>
Ada Lovelace
<TagsInput
htmlName="team"
label="Team"
onChange={() => {}}
placeholder="Search people"
hasClear
isReadOnly
/>
Ada Lovelace
<TagsInput
htmlName="team"
label="Team"
onChange={() => {}}
placeholder="Search people"
size="sm"
/>
Ada Lovelace
<TagsInput
htmlName="team"
label="Team"
onChange={() => {}}
placeholder="Search people"
size="lg"
/>
Ada Lovelace
<TagsInput
htmlName="team"
label="Team"
onChange={() => {}}
placeholder="Search people"
isRequired
/>
Ada Lovelace
<TagsInput
htmlName="team"
label="Team"
onChange={() => {}}
placeholder="Search people"
status={{message: 'At least one team member is required.', type: 'error'}}
/>
Ada Lovelace
This team has limited capacity.
<TagsInput
htmlName="team"
label="Team"
onChange={() => {}}
placeholder="Search people"
status={{message: 'This team has limited capacity.', type: 'warning'}}
/>
Ada LovelaceGrace HopperKatherine JohnsonMargaret Hamilton
function OverflowInlineStory(
args: React.ComponentProps<typeof TagsInput>,
): React.JSX.Element {
const [val, setVal] = useState<SearchableItem[]>(people.slice(0, 4));
const source = useMemo(() => createStaticSearchSource(people), []);
return (
<div style={{maxWidth: 400}}>
<TagsInput
{...args}
debounceMs={0}
hasEntriesOnFocus
onChange={(items, change) => {
setVal(items);
args.onChange(items, change);
}}
searchSource={source}
tagOverflowBehavior="unfocusedInline"
value={val}
/>
</div>
);
}
<OverflowInlineStory {...args} />
Ada LovelaceGrace HopperKatherine JohnsonMargaret HamiltonHedy Lamarr
Ada LovelaceGrace HopperKatherine JohnsonMargaret HamiltonHedy Lamarr
Ada LovelaceGrace HopperKatherine JohnsonMargaret HamiltonHedy Lamarr
function OverflowBehaviorsStory(
args: React.ComponentProps<typeof TagsInput>,
): React.JSX.Element {
const [inlineValue, setInlineValue] = useState<SearchableItem[]>(
people.slice(0, 5),
);
const [popoverValue, setPopoverValue] = useState<SearchableItem[]>(
people.slice(0, 5),
);
const source = useMemo(() => createStaticSearchSource(people), []);
return (
<div
style={{
display: 'grid',
gap: 24,
gridTemplateColumns: 'repeat(auto-fit, minmax(260px, 1fr))',
maxWidth: 720,
}}>
<TagsInput
{...args}
debounceMs={0}
hasEntriesOnFocus
label="Inline overflow"
onChange={(items, change) => {
setInlineValue(items);
args.onChange(items, change);
}}
searchSource={source}
startIcon={Users}
tagOverflowBehavior="unfocusedInline"
value={inlineValue}
/>
<TagsInput
{...args}
debounceMs={0}
hasEntriesOnFocus
label="Popover overflow"
onChange={(items, change) => {
setPopoverValue(items);
args.onChange(items, change);
}}
searchSource={source}
startIcon={Users}
tagOverflowBehavior="unfocusedLayer"
value={popoverValue}
/>
</div>
);
}
<OverflowBehaviorsStory {...args} />

Multi-select combobox that renders selected values as removable tags.

When isOptional: false, isRequired: false

PropTypeDefaultDescription
classNamestringAdditional CSS class names applied to the input wrapper.
data-testidstringTest ID applied to the input wrapper.
debounceMsnumber150Debounce delay in milliseconds before search runs.
descriptionReactNodeSupporting text rendered below the label.
emptySearchResultsTextstring'No results found'Empty state text.
endContentReactNodeContent rendered at the end of the input row.
htmlNamestringHTML name attribute for native form submission. Each selected item ID is submitted as a separate entry.
handleRefRef<TagsInputHandle>Imperative focus/blur handle.
hasAutoFocusbooleanfalseWhether to focus the input on mount.
hasClearbooleanfalseWhether to show a clear button that removes all tags.
hasEntriesOnFocusbooleanfalseWhether to show bootstrap results on focus before typing.
isDisabledbooleanfalseWhether the input is disabled.
isLabelHiddenbooleanfalseWhether to visually hide the label.
isReadOnlybooleanfalseWhether the input is read-only. Prevents typing and hides the clear button without applying disabled opacity.
label*stringField label.
labelIconIconComponentIcon shown before the label.
labelTooltipReactNodeTooltip content shown next to the label.
maxEntriesnumberMaximum number of selected tags.
maxMenuItemsnumber10Maximum number of menu items.
onBlur(event: FocusEvent<HTMLDivElement>) => voidCalled when focus leaves the tags-input.
onChange*(items: T[], change: TagsInputChange<T>) => voidCalled when selected tags change.
onFocus(event: FocusEvent<HTMLDivElement>) => voidCalled when focus enters the tags-input.
onQueryChange(query: string) => voidCalled when the query changes.
placeholderstringPlaceholder text.
refRef<HTMLDivElement>Ref forwarded to the field root.
renderItem(item: T) => ReactNodeCustom result renderer.
renderTag( item: T, onRemove: () => void, state: TagsInputTagState, ) => ReactNodeCustom tag renderer.
searchSource*SearchSource<T>Provides results for the menu. Use createStaticSearchSource for in-memory data, or implement SearchSource for async/remote search.
size"sm" | "md" | "lg"'md'Visual size.
startIconIconComponentIcon shown before the input.
statusInputStatusValidation status displayed below the tags-input.
styleCSSPropertiesInline styles applied to the input wrapper.
tagOverflowBehavior"none" | "unfocusedInline" | "unfocusedLayer"'none'Controls how tags overflow when the container is too narrow. - 'none': Tags wrap to multiple lines (default). - 'unfocusedInline': Single line with "+ N more" when unfocused; expands inline on focus. - 'unfocusedLayer': Single line with "+ N more" when unfocused; expands as overlay on focus.
value*T[]Selected items.
isOptionalfalse
isRequiredfalse
hasCreatebooleanfalseWhether users can create a tag from free text.
createItem(rawValue: string) => TBuilds the committed item from the typed text. Optional for this item type because the default builder — rawValue => ({id: rawValue, label: rawValue}) — already produces a valid item. Builds the committed item from the typed text. Required for this item type because it has fields the default {id, label} builder cannot supply.

When isOptional: true, isRequired: false

PropTypeDefaultDescription
classNamestringAdditional CSS class names applied to the input wrapper.
data-testidstringTest ID applied to the input wrapper.
debounceMsnumber150Debounce delay in milliseconds before search runs.
descriptionReactNodeSupporting text rendered below the label.
emptySearchResultsTextstring'No results found'Empty state text.
endContentReactNodeContent rendered at the end of the input row.
htmlNamestringHTML name attribute for native form submission. Each selected item ID is submitted as a separate entry.
handleRefRef<TagsInputHandle>Imperative focus/blur handle.
hasAutoFocusbooleanfalseWhether to focus the input on mount.
hasClearbooleanfalseWhether to show a clear button that removes all tags.
hasEntriesOnFocusbooleanfalseWhether to show bootstrap results on focus before typing.
isDisabledbooleanfalseWhether the input is disabled.
isLabelHiddenbooleanfalseWhether to visually hide the label.
isReadOnlybooleanfalseWhether the input is read-only. Prevents typing and hides the clear button without applying disabled opacity.
label*stringField label.
labelIconIconComponentIcon shown before the label.
labelTooltipReactNodeTooltip content shown next to the label.
maxEntriesnumberMaximum number of selected tags.
maxMenuItemsnumber10Maximum number of menu items.
onBlur(event: FocusEvent<HTMLDivElement>) => voidCalled when focus leaves the tags-input.
onChange*(items: T[], change: TagsInputChange<T>) => voidCalled when selected tags change.
onFocus(event: FocusEvent<HTMLDivElement>) => voidCalled when focus enters the tags-input.
onQueryChange(query: string) => voidCalled when the query changes.
placeholderstringPlaceholder text.
refRef<HTMLDivElement>Ref forwarded to the field root.
renderItem(item: T) => ReactNodeCustom result renderer.
renderTag( item: T, onRemove: () => void, state: TagsInputTagState, ) => ReactNodeCustom tag renderer.
searchSource*SearchSource<T>Provides results for the menu. Use createStaticSearchSource for in-memory data, or implement SearchSource for async/remote search.
size"sm" | "md" | "lg"'md'Visual size.
startIconIconComponentIcon shown before the input.
statusInputStatusValidation status displayed below the tags-input.
styleCSSPropertiesInline styles applied to the input wrapper.
tagOverflowBehavior"none" | "unfocusedInline" | "unfocusedLayer"'none'Controls how tags overflow when the container is too narrow. - 'none': Tags wrap to multiple lines (default). - 'unfocusedInline': Single line with "+ N more" when unfocused; expands inline on focus. - 'unfocusedLayer': Single line with "+ N more" when unfocused; expands as overlay on focus.
value*T[]Selected items.
isOptional*true
isRequiredfalse
hasCreatebooleanfalseWhether users can create a tag from free text.
createItem(rawValue: string) => TBuilds the committed item from the typed text. Optional for this item type because the default builder — rawValue => ({id: rawValue, label: rawValue}) — already produces a valid item. Builds the committed item from the typed text. Required for this item type because it has fields the default {id, label} builder cannot supply.

When isOptional: false, isRequired: true

PropTypeDefaultDescription
classNamestringAdditional CSS class names applied to the input wrapper.
data-testidstringTest ID applied to the input wrapper.
debounceMsnumber150Debounce delay in milliseconds before search runs.
descriptionReactNodeSupporting text rendered below the label.
emptySearchResultsTextstring'No results found'Empty state text.
endContentReactNodeContent rendered at the end of the input row.
htmlNamestringHTML name attribute for native form submission. Each selected item ID is submitted as a separate entry.
handleRefRef<TagsInputHandle>Imperative focus/blur handle.
hasAutoFocusbooleanfalseWhether to focus the input on mount.
hasClearbooleanfalseWhether to show a clear button that removes all tags.
hasEntriesOnFocusbooleanfalseWhether to show bootstrap results on focus before typing.
isDisabledbooleanfalseWhether the input is disabled.
isLabelHiddenbooleanfalseWhether to visually hide the label.
isReadOnlybooleanfalseWhether the input is read-only. Prevents typing and hides the clear button without applying disabled opacity.
label*stringField label.
labelIconIconComponentIcon shown before the label.
labelTooltipReactNodeTooltip content shown next to the label.
maxEntriesnumberMaximum number of selected tags.
maxMenuItemsnumber10Maximum number of menu items.
onBlur(event: FocusEvent<HTMLDivElement>) => voidCalled when focus leaves the tags-input.
onChange*(items: T[], change: TagsInputChange<T>) => voidCalled when selected tags change.
onFocus(event: FocusEvent<HTMLDivElement>) => voidCalled when focus enters the tags-input.
onQueryChange(query: string) => voidCalled when the query changes.
placeholderstringPlaceholder text.
refRef<HTMLDivElement>Ref forwarded to the field root.
renderItem(item: T) => ReactNodeCustom result renderer.
renderTag( item: T, onRemove: () => void, state: TagsInputTagState, ) => ReactNodeCustom tag renderer.
searchSource*SearchSource<T>Provides results for the menu. Use createStaticSearchSource for in-memory data, or implement SearchSource for async/remote search.
size"sm" | "md" | "lg"'md'Visual size.
startIconIconComponentIcon shown before the input.
statusInputStatusValidation status displayed below the tags-input.
styleCSSPropertiesInline styles applied to the input wrapper.
tagOverflowBehavior"none" | "unfocusedInline" | "unfocusedLayer"'none'Controls how tags overflow when the container is too narrow. - 'none': Tags wrap to multiple lines (default). - 'unfocusedInline': Single line with "+ N more" when unfocused; expands inline on focus. - 'unfocusedLayer': Single line with "+ N more" when unfocused; expands as overlay on focus.
value*T[]Selected items.
isOptionalfalse
isRequired*true
hasCreatebooleanfalseWhether users can create a tag from free text.
createItem(rawValue: string) => TBuilds the committed item from the typed text. Optional for this item type because the default builder — rawValue => ({id: rawValue, label: rawValue}) — already produces a valid item. Builds the committed item from the typed text. Required for this item type because it has fields the default {id, label} builder cannot supply.