Navigation
Tag Group
A row of removable, selectable little labels.
When to use it: Use it for filters, keywords, and picked options.
Example
import { useState } from "react";
import { Button, Label, Tag, TagGroup, TagList } from "@comp0/react";
import { XMarkIcon } from "@heroicons/react/16/solid";
export function Example() {
const [tags, setTags] = useState(["news", "sports", "arts", "travel"]);
const [selected, setSelected] = useState<string[]>(["news"]);
const remove = (value: string) => {
setTags((current) => current.filter((entry) => entry !== value));
setSelected((current) => current.filter((entry) => entry !== value));
};
return (
<TagGroup value={selected} onChange={setSelected} onRemove={remove}>
<Label>Topics</Label>
<TagList className="flex max-w-sm flex-wrap gap-2">
{tags.map((tag) => (
<Tag
key={tag}
value={tag}
className="group cursor-pointer rounded-full bg-zinc-100 outline-teal-600 focus-visible:outline-2 data-selected:bg-teal-100 dark:bg-zinc-800 dark:outline-teal-400 dark:data-selected:bg-teal-950 [&>[role=gridcell]]:flex [&>[role=gridcell]]:items-center [&>[role=gridcell]]:gap-1.5 [&>[role=gridcell]]:px-3 [&>[role=gridcell]]:py-1.5"
>
<span className="text-base capitalize sm:text-sm">{tag}</span>
<Button
aria-label={`Remove ${tag}`}
className="rounded-full p-0.5 text-zinc-500 hover:bg-zinc-950/10 hover:text-zinc-800 dark:text-zinc-400 dark:hover:bg-white/10 dark:hover:text-zinc-100"
onClick={() => remove(tag)}
>
<XMarkIcon className="size-3.5" aria-hidden="true" />
</Button>
</Tag>
))}
</TagList>
</TagGroup>
);
}Anatomy
Dashed frames are invisible state providers; shaded shapes own real DOM. Numbered pins match the list below.
A wireframe sketch of the assembled component. Each numbered marker matches a part in the list that follows.
TagGroup
Selection and removal provider. Does not add a DOM element.
Label
Visible name connected to TagList. Owns a DOM element.
TagList
Grid container and the tags' keyboard boundary. Owns a DOM element.
Tag
Removable, selectable label that can hold a control. Owns a DOM element.
Step by step
- 1
Add the main part
Start TagGroup with selection and removal callbacks.
- 2
Add the supporting parts
Add a visible Label, then put the tags inside TagList.
- 3
Make the behavior clear
Give every Tag a value; wire onRemove for Delete and value/onChange for selection.
Exampletsx <TagGroup onRemove={remove}><Label>Filters</Label><TagList><Tag value="news">News</Tag></TagList></TagGroup>
Keyboard
- →
- Moves to the next tag.
- ←
- Moves to the previous tag.
- Home
- Moves to the first tag.
- End
- Moves to the last tag.
- Space
- Toggles the focused tag's selection.
- ↵
- Toggles the focused tag's selection.
Forms and accessibility
Selection does not create a native form value; mirror it into hidden inputs when a form needs it.
Accessibility checklist
- Use a visible Label for the tag list, or give TagList an aria-label when no label is shown.
- Keep remove controls reachable by pointer; Delete removes from the keyboard.
- Show selection with more than color.
API reference
import { Button, Label, Tag, TagGroup, TagList } from "@comp0/react";TagGroup
Context onlySelection and removal provider.
| Prop | Type | Description |
|---|---|---|
value | string[] | Controlled selected tags. |
defaultValue | string[] | Initial selected tags. |
onChange | (value: string[]) => void | Receives the next selected tags. |
onRemove | (value: string) => void | Receives a tag removed with Delete or Backspace. |
Label
DOM elementVisible name connected to TagList.
TagList
DOM elementGrid container and the tags' keyboard boundary.
| Prop | Type | Description |
|---|---|---|
aria-label | string | Names the tag list when no visible Label is shown. |
Tag
DOM elementRemovable, selectable label that can hold a control.
| Prop | Type | Description |
|---|---|---|
value | string | This tag’s identity. |
disabled | boolean | Disables the tag. |
textValue | string | Overrides the text crawled from children when markup makes it ambiguous. |
Style hooks
Attributes that appear while a state is true. Target them with Tailwind data variants such as data-open:bg-zinc-100, or with any CSS selector.
[data-selected]on Tag
- The tag is selected.
[data-disabled]on Tag
- The tag is disabled.
:focus-visibleon Tag
- The tag has keyboard focus.