Tag Group

PreviousNext

A tag group is a focusable list of labels, categories, keywords, filters, or other items, with support for keyboard navigation, selection, and removal.

Categories
News
Travel
Gaming
Shopping
Select your categories
import { Tag, TagGroup } from "@/components/ui/tag-group"

export function TagGroupDemo() {
  return (
    <TagGroup
      label="Categories"
      description="Select your categories"
      selectionMode="single"
    >
      <Tag id="news">News</Tag>
      <Tag id="travel">Travel</Tag>
      <Tag id="gaming">Gaming</Tag>
      <Tag id="shopping">Shopping</Tag>
    </TagGroup>
  )
}

Installation

pnpm dlx shadcn@latest add tag-group

Usage

import { Tag, TagGroup } from "@/components/ui/tag-group"
<TagGroup label="Categories">
  <Tag id="news">News</Tag>
  <Tag id="travel">Travel</Tag>
  <Tag id="gaming">Gaming</Tag>
  <Tag id="shopping">Shopping</Tag>
</TagGroup>

Features

A tag group displays a collection of tags with rich user interactions. Tags can be static labels, or they can support removal and selection based on your use case.

  • Keyboard navigation – Navigate tags using arrow keys, along with page up/down, home/end, and more.
  • Removable – Tags can be removed from the group by clicking a remove button or pressing the backspace key.
  • Item selection – Support for single or multiple selection with keyboard and mouse interactions.
  • Accessible – Follows the ARIA grid pattern, with selection announcements via ARIA live region.
  • Styleable – Hover, focus, selection, and disabled states are provided for styling.

Examples

Default

Categories
News
Travel
Gaming
Shopping
Select your categories
import { Tag, TagGroup } from "@/components/ui/tag-group"

export function TagGroupDemo() {
  return (
    <TagGroup
      label="Categories"
      description="Select your categories"
      selectionMode="single"
    >
      <Tag id="news">News</Tag>
      <Tag id="travel">Travel</Tag>
      <Tag id="gaming">Gaming</Tag>
      <Tag id="shopping">Shopping</Tag>
    </TagGroup>
  )
}
<TagGroup
  label="Categories"
  description="Select your categories"
  selectionMode="single"
>
  <Tag id="news">News</Tag>
  <Tag id="travel">Travel</Tag>
  <Tag id="gaming">Gaming</Tag>
  <Tag id="shopping">Shopping</Tag>
</TagGroup>

Variants

Default
React
Vue
Angular
Default variant tags
Primary
React
Vue
Angular
Primary variant tags
Secondary
React
Vue
Angular
Secondary variant tags
Destructive
Bug
Error
Critical
Destructive variant tags
Outline
React
Vue
Angular
Outline variant tags
import { Tag, TagGroup } from "@/components/ui/tag-group"

export function TagGroupVariantsDemo() {
  return (
    <div className="flex flex-col gap-6">
      <TagGroup label="Default" description="Default variant tags">
        <Tag id="react">React</Tag>
        <Tag id="vue">Vue</Tag>
        <Tag id="angular">Angular</Tag>
      </TagGroup>

      <TagGroup label="Primary" description="Primary variant tags">
        <Tag id="react" variant="primary">
          React
        </Tag>
        <Tag id="vue" variant="primary">
          Vue
        </Tag>
        <Tag id="angular" variant="primary">
          Angular
        </Tag>
      </TagGroup>

      <TagGroup label="Secondary" description="Secondary variant tags">
        <Tag id="react" variant="secondary">
          React
        </Tag>
        <Tag id="vue" variant="secondary">
          Vue
        </Tag>
        <Tag id="angular" variant="secondary">
          Angular
        </Tag>
      </TagGroup>

      <TagGroup label="Destructive" description="Destructive variant tags">
        <Tag id="bug" variant="destructive">
          Bug
        </Tag>
        <Tag id="error" variant="destructive">
          Error
        </Tag>
        <Tag id="critical" variant="destructive">
          Critical
        </Tag>
      </TagGroup>

      <TagGroup label="Outline" description="Outline variant tags">
        <Tag id="react" variant="outline">
          React
        </Tag>
        <Tag id="vue" variant="outline">
          Vue
        </Tag>
        <Tag id="angular" variant="outline">
          Angular
        </Tag>
      </TagGroup>
    </div>
  )
}
<TagGroup label="Primary" description="Primary variant tags">
  <Tag id="react" variant="primary">React</Tag>
  <Tag id="vue" variant="primary">Vue</Tag>
  <Tag id="angular" variant="primary">Angular</Tag>
</TagGroup>
 
<TagGroup label="Secondary" description="Secondary variant tags">
  <Tag id="react" variant="secondary">React</Tag>
  <Tag id="vue" variant="secondary">Vue</Tag>
  <Tag id="angular" variant="secondary">Angular</Tag>
</TagGroup>
 
<TagGroup label="Destructive" description="Destructive variant tags">
  <Tag id="bug" variant="destructive">Bug</Tag>
  <Tag id="error" variant="destructive">Error</Tag>
  <Tag id="critical" variant="destructive">Critical</Tag>
</TagGroup>
 
<TagGroup label="Outline" description="Outline variant tags">
  <Tag id="react" variant="outline">React</Tag>
  <Tag id="vue" variant="outline">Vue</Tag>
  <Tag id="angular" variant="outline">Angular</Tag>
</TagGroup>

Available variants: default, primary, secondary, destructive, outline.

Removable Tags

Tech Stack
React
TypeScript
Tailwind CSS
Next.js
Select your tech stack
"use client"

import { useState } from "react"

import { Tag, TagGroup } from "@/components/ui/tag-group"

export function TagGroupRemovableDemo() {
  const [tags, setTags] = useState([
    { id: 1, name: "React" },
    { id: 2, name: "TypeScript" },
    { id: 3, name: "Tailwind CSS" },
    { id: 4, name: "Next.js" },
  ])

  return (
    <TagGroup<{ id: number; name: string }>
      label="Tech Stack"
      description="Select your tech stack"
      items={tags}
      onRemove={(keys) => setTags(tags.filter((tag) => !keys.has(tag.id)))}
    >
      {(item) => <Tag id={item.id}>{item.name}</Tag>}
    </TagGroup>
  )
}
"use client"
 
import { useState } from "react"
import { Tag, TagGroup } from "@/components/ui/tag-group"
 
export default function Example() {
  const [tags, setTags] = useState([
    { id: 1, name: "React" },
    { id: 2, name: "TypeScript" },
    { id: 3, name: "Tailwind CSS" },
    { id: 4, name: "Next.js" },
  ])
 
  return (
    <TagGroup
      label="Tech Stack"
      description="Click × or press backspace to remove"
      onRemove={(keys) =>
        setTags(tags.filter((tag) => !keys.has(tag.id)))
      }
      items={tags}
    >
      {(item) => <Tag id={item.id}>{item.name}</Tag>}
    </TagGroup>
  )
}

Use the onRemove prop to handle tag removal. The remove button appears automatically when this prop is provided. Users can click the × button or press backspace to remove tags.

Selection

Interests
Sports
Music
Travel
Gaming
Technology
Select your interests
"use client"

import { useState } from "react"

import { Tag, TagGroup } from "@/components/ui/tag-group"

export function TagGroupSelectionDemo() {
  const [selected, setSelected] = useState<Set<string>>(
    new Set(["sports", "music"])
  )

  return (
    <TagGroup
      selectionMode="multiple"
      selectedKeys={selected}
      onSelectionChange={(keys) => setSelected(keys as Set<string>)}
      label="Interests"
      description="Select your interests"
    >
      <Tag id="sports">Sports</Tag>
      <Tag id="music">Music</Tag>
      <Tag id="travel">Travel</Tag>
      <Tag id="gaming">Gaming</Tag>
      <Tag id="technology">Technology</Tag>
    </TagGroup>
  )
}
"use client"
 
import { useState } from "react"
import { Tag, TagGroup } from "@/components/ui/tag-group"
 
export default function Example() {
  const [selected, setSelected] = useState<Set<string>>(
    new Set(["sports", "music"])
  )
 
  return (
    <TagGroup
      selectionMode="multiple"
      selectedKeys={selected}
      onSelectionChange={(keys) => setSelected(keys as Set<string>)}
      label="Interests"
      description="Select your interests"
    >
      <Tag id="sports">Sports</Tag>
      <Tag id="music">Music</Tag>
      <Tag id="travel">Travel</Tag>
      <Tag id="gaming">Gaming</Tag>
      <Tag id="technology">Technology</Tag>
    </TagGroup>
  )
}

Set selectionMode to "multiple" to enable multiple selection, or "single" for single selection. Use the selectedKeys and onSelectionChange props to manage the selection state.

Quick LinksSelect your quick links
import Link from "next/link"

import { Tag, TagGroup } from "@/components/ui/tag-group"

export function TagGroupLinksDemo() {
  return (
    <TagGroup label="Quick Links" description="Select your quick links">
      <Tag id="docs">
        <Link href="/docs">Documentation</Link>
      </Tag>
      <Tag id="components">
        <Link href="/components">Components</Link>
      </Tag>
      <Tag id="examples">
        <Link href="/examples">Examples</Link>
      </Tag>
      <Tag id="blocks">
        <Link href="/blocks">Blocks</Link>
      </Tag>
    </TagGroup>
  )
}
import Link from "next/link"
import { Tag, TagGroup } from "@/components/ui/tag-group"
 
<TagGroup label="Quick Links" description="Navigate to different pages">
  <Tag id="docs" href="/docs">
    <Link href="/docs">Documentation</Link>
  </Tag>
  <Tag id="components" href="/docs/components">
    <Link href="/docs/components">Components</Link>
  </Tag>
  <Tag id="examples" href="/examples">
    <Link href="/examples">Examples</Link>
  </Tag>
  <Tag id="blocks" href="/blocks">
    <Link href="/blocks">Blocks</Link>
  </Tag>
</TagGroup>

Tags can be used as navigation links by providing an href prop and wrapping the content with a Next.js Link component.

Disabled Tags

Categories
Active
Disabled
Select your categories
import { Tag, TagGroup } from "@/components/ui/tag-group"

export function TagGroupDisabledDemo() {
  return (
    <TagGroup label="Categories" description="Select your categories">
      <Tag id="active">Active</Tag>
      <Tag id="disabled" isDisabled>
        Disabled
      </Tag>
    </TagGroup>
  )
}
<TagGroup label="Categories" description="Select your categories">
  <Tag id="active1">Active</Tag>
  <Tag id="disabled1" isDisabled>Disabled</Tag>
  <Tag id="active2">Active</Tag>
  <Tag id="disabled2" isDisabled>Disabled</Tag>
</TagGroup>

Use the isDisabled prop on individual tags to disable them. Disabled tags cannot be selected, removed, or interacted with.

Empty State

Tags
No tags available
Select your tags
"use client"

import { TagGroup } from "@/components/ui/tag-group"

export function TagGroupEmptyDemo() {
  return (
    <TagGroup
      label="Tags"
      description="Select your tags"
      renderEmptyState={() => <div>No tags available</div>}
    ></TagGroup>
  )
}
<TagGroup
  label="Tags"
  description="Available tags"
  renderEmptyState={() => (
    <div className="flex min-h-[100px] items-center justify-center rounded-md border border-dashed">
      <p className="text-sm text-muted-foreground">No tags available</p>
    </div>
  )}
>
  {/* Empty - will show renderEmptyState */}
</TagGroup>

Use the renderEmptyState prop to show a custom empty state when there are no tags.

API Reference

TagGroup

The TagGroup component is the container for a collection of tags.

PropTypeDefaultDescription
labelstring-Label text for the tag group.
descriptionstring-Description text displayed below the label.
errorMessagestring-Error message to display.
selectionMode"none" | "single" | "multiple""none"Whether tags can be selected and what selection mode to use.
selectedKeysIterable<Key>-The currently selected keys (controlled).
onSelectionChange(keys: Selection) => void-Handler called when the selection changes.
disabledKeysIterable<Key>-Keys of tags that are disabled.
onRemove(keys: Set<Key>) => void-Handler called when tags are removed. Enables remove buttons.
itemsIterable<T>-Item objects for dynamic collections.
renderEmptyState() => React.ReactNode-Renders when the list is empty.

Tag

Individual tag items within a tag group.

PropTypeDefaultDescription
idKey-A unique identifier for the tag (required for selection/removal).
variant"default" | "primary" | "secondary" | "destructive" | "outline""default"The visual style variant of the tag.
textValuestring-A string representation for accessibility and typeahead.
hrefstring-URL to navigate to (makes the tag a link).
isDisabledbooleanfalseWhether the tag is disabled.

Apart from the props above, the components support all props from the react-aria-components library. Check the React Aria documentation for more details.

Accessibility

Tag groups follow the ARIA grid pattern, which provides keyboard navigation, selection announcements, and focus management.

  • Keyboard Navigation – Arrow keys navigate between tags. Home/End move to first/last tag.
  • Removal – Press backspace when focused on a tag to remove it (if onRemove is provided).
  • Selection – Space or Enter keys select tags when selection is enabled.
  • Screen Readers – Selection state and removal actions are announced via ARIA live regions.

Always provide a label prop or use aria-label to identify the tag group:

<TagGroup label="Categories">
  <Tag id="news">News</Tag>
  <Tag id="travel">Travel</Tag>
</TagGroup>