Tags Input
Used to enter multiple values as tags with features like tag creation, deletion, and keyboard navigation.
"use client"
import { Span, TagsInput } from "@chakra-ui/react"
const Demo = () => {
return (
<TagsInput.Root defaultValue={["React", "Chakra", "TypeScript"]}>
<TagsInput.Label>Tags</TagsInput.Label>
<TagsInput.Control>
<TagsInput.Items />
<TagsInput.Input placeholder="Add tag..." />
</TagsInput.Control>
<Span textStyle="xs" color="fg.muted" ms="auto">
Press Enter or Return to add tag
</Span>
</TagsInput.Root>
)
}
Usage
import { TagsInput } from "@chakra-ui/react"
<TagsInput.Root>
<TagsInput.Label />
<TagsInput.Control>
<TagsInput.Item>
<TagsInput.ItemPreview>
<TagsInput.ItemText />
<TagsInput.ItemDeleteTrigger />
</TagsInput.ItemPreview>
<TagsInput.ItemInput />
</TagsInput.Item>
<TagsInput.Input />
</TagsInput.Control>
</TagsInput.Root>
Shortcuts
The TagsInput
component also provides a set of shortcuts for common use cases.
TagsInputItems
The TagsInputItems
shortcut renders all tag items automatically based on the
current value.
This works:
<TagsInput.Context>
{({ value }) =>
value.map((tag, index) => (
<TagsInput.Item key={index} index={index} value={tag}>
<TagsInput.ItemPreview>
<TagsInput.ItemText>{tag}</TagsInput.ItemText>
<TagsInput.ItemDeleteTrigger />
</TagsInput.ItemPreview>
<TagsInput.ItemInput />
</TagsInput.Item>
))
}
</TagsInput.Context>
This might be more concise, if you don't need to customize the items:
<TagsInput.Items />
Examples
Sizes
Use the size
prop to adjust the size of the tags input.
"use client"
import { For, Span, Stack, TagsInput } from "@chakra-ui/react"
const Demo = () => {
return (
<Stack>
<For each={["xs", "sm", "md", "lg"]}>
{(size) => (
<TagsInput.Root
key={size}
size={size}
readOnly
defaultValue={["React", "Chakra", "TypeScript"]}
>
<TagsInput.Label>Tags (size={size})</TagsInput.Label>
<TagsInput.Control>
<TagsInput.Items />
<TagsInput.Input placeholder="Add tag..." />
</TagsInput.Control>
<Span textStyle="xs" color="fg.muted" ms="auto">
Press Enter or Return to add tag
</Span>
</TagsInput.Root>
)}
</For>
</Stack>
)
}
Variants
Use the variant
prop to change the visual style of the tags input.
"use client"
import { For, Span, Stack, TagsInput } from "@chakra-ui/react"
const Demo = () => {
return (
<Stack>
<For each={["outline", "subtle", "flushed"]}>
{(variant) => (
<TagsInput.Root
key={variant}
variant={variant}
readOnly
defaultValue={["React", "Chakra", "TypeScript"]}
>
<TagsInput.Label>Tags (variant={variant})</TagsInput.Label>
<TagsInput.Control>
<TagsInput.Items />
<TagsInput.Input placeholder="Add tag..." />
</TagsInput.Control>
<Span textStyle="xs" color="fg.muted" ms="auto">
Press Enter or Return to add tag
</Span>
</TagsInput.Root>
)}
</For>
</Stack>
)
}
Controlled
Use the value
and onValueChange
props to programmatically control the tags.
"use client"
import { TagsInput } from "@chakra-ui/react"
import { useState } from "react"
const Demo = () => {
const [tags, setTags] = useState<string[]>(["React", "Chakra"])
return (
<TagsInput.Root
value={tags}
onValueChange={(details) => setTags(details.value)}
>
<TagsInput.Label>Tags</TagsInput.Label>
<TagsInput.Control>
<TagsInput.Items />
<TagsInput.Input placeholder="Add tag..." />
</TagsInput.Control>
</TagsInput.Root>
)
}
Store
An alternative way to control the tags input is to use the RootProvider
component and the useTagsInput
store hook.
This way you can access the tags input state and methods from outside the component.
Use RootProvider + useTagsInput
or Root
, not both.
"use client"
import { Button, Stack, TagsInput } from "@chakra-ui/react"
import { useTagsInput } from "@chakra-ui/react"
const Demo = () => {
const tags = useTagsInput()
return (
<Stack align="flex-start" gap="4">
<TagsInput.RootProvider value={tags}>
<TagsInput.Label>Tags: {JSON.stringify(tags.value)}</TagsInput.Label>
<TagsInput.Control>
<TagsInput.Items />
<TagsInput.Input placeholder="Add tag..." />
</TagsInput.Control>
</TagsInput.RootProvider>
<Button variant="outline" size="sm" onClick={() => tags.clearValue()}>
Clear All
</Button>
</Stack>
)
}
Max Tags
Pass the max
prop to the TagsInput.Root
component to limit the number of
tags that can be added.
"use client"
import { Badge, Button, HStack, Span, TagsInput } from "@chakra-ui/react"
const EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
const isValidEmail = (value: string) => EMAIL_REGEX.test(value)
const Demo = () => {
return (
<TagsInput.Root
max={3}
validate={(e) => isValidEmail(e.inputValue)}
defaultValue={["sage@company.com"]}
>
<TagsInput.Label>Invite guests (max 3)</TagsInput.Label>
<TagsInput.Control>
<TagsInput.Items />
<TagsInput.Input placeholder="Add guests" />
</TagsInput.Control>
<TagsInput.Context>
{({ value }) => (
<HStack justify="space-between" hidden={value.length === 0} mt="2.5">
<Span>
You've invited <Badge>{value.length} / 3 guests</Badge> to your
event
</Span>
<Button size="sm">Invite</Button>
</HStack>
)}
</TagsInput.Context>
</TagsInput.Root>
)
}
Editable Tags
Use the editable
prop to enable inline editing of existing tags by clicking on
them, allowing users to quickly update tag values.
"use client"
import { Span, TagsInput } from "@chakra-ui/react"
export const TagsInputEditable = () => (
<TagsInput.Root editable defaultValue={["React", "Chakra"]}>
<TagsInput.Label>Edit Tags Inline</TagsInput.Label>
<TagsInput.Control>
<TagsInput.Items />
<TagsInput.Input placeholder="Add or edit tags..." />
<TagsInput.ClearTrigger />
</TagsInput.Control>
<TagsInput.HiddenInput />
<Span textStyle="xs" color="fg.muted" ms="auto">
Use the arrow keys to navigate and press Enter to edit
</Span>
</TagsInput.Root>
)
Validate Tag
Use the validate
prop to implement custom validation rules. Tags will be added
when the validation passes.
"use client"
import { TagsInput } from "@chakra-ui/react"
export const TagsInputValidation = () => (
<TagsInput.Root
defaultValue={["React", "Chakra"]}
validate={(e) => e.inputValue.length >= 3}
>
<TagsInput.Label>Tags (min 3 chars)</TagsInput.Label>
<TagsInput.Control>
<TagsInput.Items />
<TagsInput.Input placeholder="Add a tag..." />
</TagsInput.Control>
<TagsInput.HiddenInput />
</TagsInput.Root>
)
Disabled
Use the disabled
prop to disable the tags input to prevent user interaction.
"use client"
import { TagsInput } from "@chakra-ui/react"
export const TagsInputDisabled = () => (
<TagsInput.Root disabled defaultValue={["React", "Chakra"]}>
<TagsInput.Label>Disabled Tags</TagsInput.Label>
<TagsInput.Control>
<TagsInput.Items />
<TagsInput.Input placeholder="Can't type here" />
</TagsInput.Control>
<TagsInput.HiddenInput />
</TagsInput.Root>
)
Readonly
Use the readOnly
prop to make the tags input read-only. Tags input can be
focused but can't be modified.
"use client"
import { TagsInput } from "@chakra-ui/react"
export const TagsInputReadOnly = () => (
<TagsInput.Root readOnly defaultValue={["React", "Chakra"]}>
<TagsInput.Label>Read Only Tags</TagsInput.Label>
<TagsInput.Control>
<TagsInput.Items />
<TagsInput.Input placeholder="Read-only..." />
</TagsInput.Control>
<TagsInput.HiddenInput />
</TagsInput.Root>
)
Invalid
Pass the invalid
prop to the TagsInput.Root
component to display the tags
input in an invalid state with error messages.
"use client"
import { Field, TagsInput } from "@chakra-ui/react"
export const TagsInputInvalid = () => (
<Field.Root invalid>
<TagsInput.Root defaultValue={["React", "Chakra"]}>
<TagsInput.Label>Invalid Tags</TagsInput.Label>
<TagsInput.Control>
<TagsInput.Items />
<TagsInput.Input placeholder="Add tags..." />
</TagsInput.Control>
<TagsInput.HiddenInput />
</TagsInput.Root>
<Field.ErrorText>This is an error</Field.ErrorText>
</Field.Root>
)
Field
Here's an example that composes the TagsInput.Root
with the Field
component
to add labels, helper text, and error messages.
"use client"
import { Field, TagsInput } from "@chakra-ui/react"
const Demo = () => {
return (
<Field.Root>
<TagsInput.Root defaultValue={["React", "Chakra", "TypeScript"]}>
<TagsInput.Label>Enter tags</TagsInput.Label>
<TagsInput.Control>
<TagsInput.Items />
<TagsInput.Input placeholder="Add tag..." />
</TagsInput.Control>
</TagsInput.Root>
<Field.HelperText>Add emails separated by commas</Field.HelperText>
</Field.Root>
)
}
Form
Here's an example that composes the TagsInput.Root
with a form
to collect
structured data and handle submissions.
"use client"
import { Button, Field, Input, Stack, TagsInput } from "@chakra-ui/react"
const Demo = () => {
return (
<form
onSubmit={(e) => {
e.preventDefault()
const formData = new FormData(e.currentTarget)
const title = formData.get("title")
const categories = formData.get("categories")
console.log("Submitted formData:", { title, categories })
}}
>
<Stack gap="4">
<Field.Root>
<Field.Label>Title</Field.Label>
<Input name="title" />
</Field.Root>
<Field.Root>
<TagsInput.Root name="categories">
<TagsInput.Label>Categories</TagsInput.Label>
<TagsInput.Control>
<TagsInput.Items />
<TagsInput.Input placeholder="Add tag..." />
</TagsInput.Control>
<TagsInput.HiddenInput />
</TagsInput.Root>
<Field.HelperText>
Add frameworks and libraries you use
</Field.HelperText>
</Field.Root>
<Button type="submit" variant="solid" mt="3">
Submit
</Button>
</Stack>
</form>
)
}
Paste
Pass the addOnPaste
prop to the TagsInput.Root
component to allow users to
paste multiple values at once, automatically splitting them into individual tags
based on a delimiter.
"use client"
import { Box, Clipboard, IconButton, Stack, TagsInput } from "@chakra-ui/react"
export const TagsInputWithPaste = () => (
<Stack gap="8">
<SampleClipboard value="React,Chakra,TypeScript" />
<TagsInput.Root addOnPaste delimiter=",">
<TagsInput.Label>Paste Tags</TagsInput.Label>
<TagsInput.Control>
<TagsInput.Items />
<TagsInput.Input placeholder="Paste" />
<TagsInput.ClearTrigger />
</TagsInput.Control>
<TagsInput.HiddenInput />
</TagsInput.Root>
</Stack>
)
const SampleClipboard = (props: { value: string }) => (
<Clipboard.Root value={props.value}>
<Box textStyle="label" mb="2">
Copy Tags
</Box>
<Clipboard.ValueText me="3" textStyle="sm" fontFamily="mono" />
<Clipboard.Trigger asChild>
<IconButton variant="surface" size="2xs">
<Clipboard.Indicator />
</IconButton>
</Clipboard.Trigger>
</Clipboard.Root>
)
Blur Behavior
Use the blurBehavior
prop to configure how the input behaves when it loses
focus, such as automatically creating a tag from the current input value.
"use client"
import { TagsInput } from "@chakra-ui/react"
export const TagsInputWithBlurBehavior = () => (
<TagsInput.Root blurBehavior="add">
<TagsInput.Label>Create Tag on Blur</TagsInput.Label>
<TagsInput.Control>
<TagsInput.Items />
<TagsInput.Input placeholder="Type and blur to create tag..." />
<TagsInput.ClearTrigger />
</TagsInput.Control>
<TagsInput.HiddenInput />
</TagsInput.Root>
)
Custom Delimiter
Use the delimiter
prop to use custom delimiters like commas, semicolons, or
spaces to create new tags as users type.
"use client"
import { TagsInput } from "@chakra-ui/react"
const SPLIT_REGEX = /[;,]/
export const TagsInputWithDelimiter = () => (
<TagsInput.Root delimiter={SPLIT_REGEX}>
<TagsInput.Label>Custom Delimiters (; ,)</TagsInput.Label>
<TagsInput.Control>
<TagsInput.Items />
<TagsInput.Input placeholder="Type and use ; or , to create tag..." />
<TagsInput.ClearTrigger />
</TagsInput.Control>
<TagsInput.HiddenInput />
</TagsInput.Root>
)
Colors
Here's an example that assigns rather color scheme to the tags based on the tag value.
"use client"
import { TagsInput } from "@chakra-ui/react"
export const TagsInputWithColors = () => (
<TagsInput.Root defaultValue={["React", "Chakra", "TypeScript"]}>
<TagsInput.Label>Colored Tags</TagsInput.Label>
<TagsInput.Control>
<TagsInput.Context>
{({ value }) =>
value.map((tag, index) => (
<TagsInput.Item key={index} index={index} value={tag}>
<TagsInput.ItemPreview
style={{ backgroundColor: randomColor(tag) }}
_highlighted={{ filter: "brightness(0.9)" }}
>
<TagsInput.ItemText>{tag}</TagsInput.ItemText>
<TagsInput.ItemDeleteTrigger />
</TagsInput.ItemPreview>
<TagsInput.ItemInput />
</TagsInput.Item>
))
}
</TagsInput.Context>
<TagsInput.Input placeholder="Add tag..." />
<TagsInput.ClearTrigger />
</TagsInput.Control>
<TagsInput.HiddenInput />
</TagsInput.Root>
)
const randomColor = (str: string) => {
// Simple hash from string
let hash = 0
for (let i = 0; i < str.length; i++) {
hash = str.charCodeAt(i) + ((hash << 5) - hash)
}
// Generate light HSL color (H: 0-359, S: 60-80%, L: 85-94%)
const h = Math.abs(hash) % 360
const s = 60 + (Math.abs(hash) % 20) // 60% - 79%
const l = 85 + (Math.abs(hash) % 10) // 85% - 94%
return `hsl(${h},${s}%,${l}%)`
}
Combobox
Here's an example that composes the TagsInput
component with the Combobox
component to create a tags input that allows users to select from a list of
predefined tags.
"use client"
import {
Combobox,
TagsInput,
useCombobox,
useFilter,
useListCollection,
useTagsInput,
} from "@chakra-ui/react"
import { useId, useRef } from "react"
const Demo = () => {
const { contains } = useFilter({ sensitivity: "base" })
const { collection, filter } = useListCollection({
initialItems: [
"React",
"Chakra",
"TypeScript",
"Next.js",
"Ark UI",
"Zag.js",
],
filter: contains,
})
const inputId = useId()
const controlRef = useRef<HTMLDivElement | null>(null)
const tags = useTagsInput({
ids: { input: inputId },
})
const comobobox = useCombobox({
ids: { input: inputId },
collection,
onInputValueChange(e) {
filter(e.inputValue)
},
positioning: {
getAnchorRect() {
return controlRef.current!.getBoundingClientRect()
},
},
value: [],
allowCustomValue: true,
onValueChange: (e) => tags.addValue(e.value[0]),
selectionBehavior: "clear",
})
return (
<Combobox.RootProvider value={comobobox}>
<TagsInput.RootProvider value={tags}>
<TagsInput.Label>Tags</TagsInput.Label>
<TagsInput.Control ref={controlRef}>
{tags.value.map((tag, index) => (
<TagsInput.Item key={index} index={index} value={tag}>
<TagsInput.ItemPreview>
<TagsInput.ItemText>{tag}</TagsInput.ItemText>
<TagsInput.ItemDeleteTrigger />
</TagsInput.ItemPreview>
</TagsInput.Item>
))}
<Combobox.Input unstyled asChild>
<TagsInput.Input placeholder="Add tag..." />
</Combobox.Input>
</TagsInput.Control>
<Combobox.Positioner>
<Combobox.Content>
<Combobox.Empty>No tags found</Combobox.Empty>
{collection.items.map((item) => (
<Combobox.Item item={item} key={item}>
<Combobox.ItemText>{item}</Combobox.ItemText>
<Combobox.ItemIndicator />
</Combobox.Item>
))}
</Combobox.Content>
</Combobox.Positioner>
</TagsInput.RootProvider>
</Combobox.RootProvider>
)
}
Props
Explorer
Explore the TagsInput
component parts interactively. Click on parts in the
sidebar to highlight them in the preview.
Component Anatomy
Hover to highlight, click to select parts
root
label
control
input
clearTrigger
item
itemPreview
itemInput
itemText
itemDeleteTrigger
tags-input.recipe.ts