Skip to main content
Tags are used to categorize items, display metadata, and create filterable lists. They support icons, removal functionality, and interactive states.

Import

import { Tag } from '@/components/ui/Tag';

Usage

Basic Tag

<Tag>Default Tag</Tag>

Variants

Tag supports eight different variants:
<Tag variant="default">Default</Tag>
<Tag variant="primary">Primary</Tag>
<Tag variant="secondary">Secondary</Tag>
<Tag variant="success">Success</Tag>
<Tag variant="warning">Warning</Tag>
<Tag variant="danger">Danger</Tag>
<Tag variant="outline">Outline</Tag>
<Tag variant="filled">Filled</Tag>

Sizes

Three sizes are available:
<Tag size="sm">Small</Tag>
<Tag size="md">Medium</Tag>
<Tag size="lg">Large</Tag>

With Icons

Add icons to the left or right side of tags:
import { Hash, Star, User, Calendar } from 'lucide-react';

<Tag leftIcon={<Hash size={14} />}>Category</Tag>
<Tag leftIcon={<User size={14} />}>User</Tag>
<Tag leftIcon={<Calendar size={14} />}>Date</Tag>
<Tag rightIcon={<Star size={14} />}>Favorite</Tag>

Removable Tags

Make tags removable with a close button:
<Tag
  variant="primary"
  removable
  onRemove={() => console.log('Tag removed')}
>
  Removable Tag
</Tag>
Example with state management:
const [tags, setTags] = useState([
  { id: 1, label: 'React', variant: 'primary' },
  { id: 2, label: 'TypeScript', variant: 'secondary' },
  { id: 3, label: 'Design System', variant: 'success' },
]);

const removeTag = (id) => {
  setTags(tags.filter((tag) => tag.id !== id));
};

return (
  <div className="flex gap-2">
    {tags.map((tag) => (
      <Tag
        key={tag.id}
        variant={tag.variant}
        removable
        onRemove={() => removeTag(tag.id)}
      >
        {tag.label}
      </Tag>
    ))}
  </div>
);

Interactive Tags

Make tags clickable for selection or filtering:
const [selectedTags, setSelectedTags] = useState([]);

const toggleTag = (tag) => {
  setSelectedTags((prev) =>
    prev.includes(tag) ? prev.filter((t) => t !== tag) : [...prev, tag]
  );
};

const tags = ['React', 'Vue', 'Angular', 'Svelte'];

return (
  <div className="flex gap-2">
    {tags.map((tag) => (
      <Tag
        key={tag}
        variant={selectedTags.includes(tag) ? 'primary' : 'outline'}
        interactive
        onClick={() => toggleTag(tag)}
      >
        {tag}
      </Tag>
    ))}
  </div>
);

Examples

Skills & Technologies

import { Hash } from 'lucide-react';

<div className="flex flex-wrap gap-2">
  <Tag variant="primary" leftIcon={<Hash size={12} />}>JavaScript</Tag>
  <Tag variant="primary" leftIcon={<Hash size={12} />}>TypeScript</Tag>
  <Tag variant="secondary" leftIcon={<Hash size={12} />}>React</Tag>
  <Tag variant="success" leftIcon={<Hash size={12} />}>Node.js</Tag>
  <Tag variant="warning" leftIcon={<Hash size={12} />}>Python</Tag>
</div>

Project Status

<div className="flex gap-2">
  <Tag variant="success" size="sm">Active</Tag>
  <Tag variant="warning" size="sm">In Progress</Tag>
  <Tag variant="danger" size="sm">Blocked</Tag>
  <Tag variant="outline" size="sm">On Hold</Tag>
</div>

User Roles

import { User } from 'lucide-react';

<div className="flex gap-2">
  <Tag variant="filled" leftIcon={<User size={14} />}>Admin</Tag>
  <Tag variant="primary" leftIcon={<User size={14} />}>Editor</Tag>
  <Tag variant="secondary" leftIcon={<User size={14} />}>Viewer</Tag>
  <Tag variant="outline" leftIcon={<User size={14} />}>Guest</Tag>
</div>

Filter Tags

<div className="flex gap-2">
  <Tag
    variant="primary"
    removable
    onRemove={() => console.log('Removed')}
  >
    Category: Design
  </Tag>
  <Tag
    variant="secondary"
    removable
    onRemove={() => console.log('Removed')}
  >
    Author: John Doe
  </Tag>
  <Tag
    variant="success"
    removable
    onRemove={() => console.log('Removed')}
  >
    Status: Published
  </Tag>
</div>

Props

variant
string
default:"default"
The visual style variant. Options: default, primary, secondary, success, warning, danger, outline, filled.
size
string
default:"md"
The size of the tag. Options: sm, md, lg.
removable
boolean
default:"false"
Whether the tag can be removed. When true, displays a close button.
onRemove
() => void
Callback function called when the remove button is clicked. Only works when removable is true.
interactive
boolean
default:"false"
Whether the tag is clickable. Adds hover effects and cursor pointer.
leftIcon
ReactNode
Icon or element to display on the left side of the tag.
rightIcon
ReactNode
Icon or element to display on the right side of the tag. Not shown when removable is true.
onClick
(event: React.MouseEvent) => void
Click handler for the tag. Automatically makes the tag interactive.
className
string
Additional CSS classes to apply to the tag.
children
ReactNode
The content to display inside the tag.

Semantic Tokens

Tag uses the following semantic tokens from the Stride Design System:
  • --bg-secondary, --bg-brand-muted, --bg-tertiary, --bg-brand
  • --text-primary, --text-brand, --text-secondary, --text-inverse
  • --border-primary, --border-brand, --border-secondary
  • --status-success, --status-warning, --status-danger (and their -bg and -text variants)
  • --tag-height-sm, --tag-height-md, --tag-height-lg
  • --font-size-xs, --font-size-sm, --font-size-md
  • --interactive-ghost-hover
  • --transition-normal, --font-weight-medium

Accessibility

  • Tag is rendered as a <span> element with proper semantic structure
  • Remove button has aria-label="Remove tag" for screen readers
  • Remove button includes focus management with ring on focus
  • Interactive tags receive proper hover and active states
  • Icon sizes scale appropriately with tag size for readability
  • Close button is properly sized for touch targets (minimum 44x44px on mobile)

Build docs developers (and LLMs) love