Join our Discord Community

Badge

Displays a badge or a component that looks like a badge.

Loading component...

Installation

npx shadcn@latest add badge

Usage

import { Badge } from "@/components/ui/badge"

export function BadgeDemo() {
  return <Badge variant="default">Badge</Badge>
}

Examples

Variants

Loading component...

With Icons

Loading component...

Interactive

Loading component...

Shapes & Styles

Loading component...

API Reference

Badge

A versatile badge component with multiple variants and styles.

PropTypeDefaultDescription
variant"default" | "secondary" | "destructive" | "outline""default"The visual style variant of the badge.
asChildbooleanfalseRender as a child component (useful for links).
classNamestring-Additional CSS classes for styling.
childrenReact.ReactNode-Badge content (text, icons, or components).

Variants

Default

Primary branded appearance with high contrast.

<Badge>Default Badge</Badge>

Secondary

Neutral appearance with muted colors.

<Badge variant="secondary">Secondary Badge</Badge>

Destructive

Error or warning states with destructive colors.

<Badge variant="destructive">Destructive Badge</Badge>

Outline

Subtle appearance with border styling.

<Badge variant="outline">Outline Badge</Badge>

Implementation Details

Architecture

Built with class-variance-authority for robust variant management:

  • Flexible Composition: Uses Radix Slot for asChild functionality
  • Icon Integration: Automatic sizing and spacing for icons
  • Focus Management: Built-in focus states for interactive badges
  • Responsive Design: Scales appropriately across screen sizes

Styling System

The badge uses a comprehensive CSS class system:

// Base styles include:
// - Inline flex layout with center alignment
// - Rounded corners and border
// - Text sizing and font weight
// - Icon sizing ([&>svg]:size-3)
// - Focus and hover states
// - Transition animations

Icon Handling

Icons are automatically styled within badges:

<Badge>
  <CheckIcon /> {/* Automatically sized to 12px (size-3) */}
  Completed
</Badge>

Best Practices

Content Guidelines

  1. Keep text concise: Single words or short phrases work best
  2. Use meaningful labels: Provide context for the badge purpose
  3. Consider accessibility: Ensure sufficient color contrast
  4. Consistent sizing: Maintain visual hierarchy with appropriate variants

Visual Hierarchy

// Primary information - use default
<Badge>Important</Badge>

// Secondary information - use secondary or outline
<Badge variant="secondary">Optional</Badge>
<Badge variant="outline">Metadata</Badge>

// Errors or warnings - use destructive
<Badge variant="destructive">Error</Badge>

Icon Placement

Icons can be placed before, after, or on both sides of text:

// Leading icon (recommended)
<Badge><CheckIcon />Verified</Badge>

// Trailing icon
<Badge>External<ExternalLinkIcon /></Badge>

// Both sides (use sparingly)
<Badge><StarIcon />Premium<CrownIcon /></Badge>

Common Use Cases

Status Indicators

Perfect for showing states and conditions:

function StatusBadge({ status }: { status: 'active' | 'pending' | 'inactive' }) {
  const variants = {
    active: { variant: 'default' as const, icon: CheckIcon, text: 'Active' },
    pending: { variant: 'secondary' as const, icon: ClockIcon, text: 'Pending' },
    inactive: { variant: 'outline' as const, icon: XIcon, text: 'Inactive' }
  }
  
  const config = variants[status]
  
  return (
    <Badge variant={config.variant}>
      <config.icon />
      {config.text}
    </Badge>
  )
}

Notification Counters

Circular badges for counts and numbers:

function NotificationBadge({ count }: { count: number }) {
  return (
    <Badge 
      variant={count > 99 ? 'destructive' : 'default'}
      className="h-6 w-6 rounded-full p-0 flex items-center justify-center text-xs font-bold"
    >
      {count > 99 ? '99+' : count}
    </Badge>
  )
}

Tag Lists

For categorization and filtering:

function TagList({ tags, onRemove }: { 
  tags: string[]
  onRemove?: (tag: string) => void 
}) {
  return (
    <div className="flex flex-wrap gap-2">
      {tags.map(tag => (
        <Badge key={tag} variant="secondary" className="gap-1">
          {tag}
          {onRemove && (
            <button
              onClick={() => onRemove(tag)}
              className="ml-1 hover:bg-secondary-foreground/20 rounded-full p-0.5"
              aria-label={`Remove ${tag} tag`}
            >
              <XIcon className="size-3" />
            </button>
          )}
        </Badge>
      ))}
    </div>
  )
}

Using the asChild prop for navigation:

function LinkBadge() {
  return (
    <Badge asChild>
      <Link href="/profile" className="hover:underline">
        <UserIcon />
        View Profile
      </Link>
    </Badge>
  )
}

Product Labels

E-commerce and feature highlighting:

function ProductBadges({ product }) {
  return (
    <div className="flex gap-2">
      {product.isNew && (
        <Badge className="bg-green-500 hover:bg-green-600">
          <StarIcon />
          New
        </Badge>
      )}
      {product.onSale && (
        <Badge variant="destructive">
          <PercentIcon />
          Sale
        </Badge>
      )}
      {product.isFeatured && (
        <Badge className="bg-purple-500 hover:bg-purple-600">
          <CrownIcon />
          Featured
        </Badge>
      )}
    </div>
  )
}

Advanced Patterns

Gradient Badges

Custom styling with gradients:

<Badge className="bg-gradient-to-r from-purple-500 to-pink-500 hover:from-purple-600 hover:to-pink-600">
  <StarIcon />
  Premium
</Badge>

Animated Badges

Adding motion for attention:

<Badge className="animate-pulse bg-red-500 hover:bg-red-600">
  <AlertIcon />
  Live
</Badge>

// Or with custom animation
<Badge className="relative overflow-hidden">
  <div className="absolute inset-0 bg-gradient-to-r from-transparent via-white/20 to-transparent animate-shimmer" />
  <StarIcon />
  Special
</Badge>

Stacked Badges

Multiple badges in hierarchy:

function StackedBadges() {
  return (
    <div className="relative inline-flex">
      <Badge>Primary</Badge>
      <Badge 
        variant="destructive" 
        className="absolute -top-2 -right-2 h-5 w-5 rounded-full p-0 flex items-center justify-center text-xs"
      >
        3
      </Badge>
    </div>
  )
}

Conditional Badges

Dynamic display based on conditions:

function ConditionalBadges({ user }) {
  return (
    <div className="flex gap-2">
      <Badge>{user.name}</Badge>
      
      {user.isVerified && (
        <Badge className="bg-blue-500 hover:bg-blue-600">
          <CheckIcon />
          Verified
        </Badge>
      )}
      
      {user.isPremium && (
        <Badge className="bg-gold-500 hover:bg-gold-600">
          <CrownIcon />
          Premium
        </Badge>
      )}
      
      {user.isOnline ? (
        <Badge className="bg-green-500 hover:bg-green-600">Online</Badge>
      ) : (
        <Badge variant="outline">Offline</Badge>
      )}
    </div>
  )
}

Accessibility

Screen Reader Support

Badges automatically provide semantic meaning:

// Good - descriptive content
<Badge><CheckIcon />Verified Account</Badge>

// Better - with aria-label for complex badges
<Badge aria-label="Account verified with email confirmation">
  <CheckIcon />
  Verified
</Badge>

// Best - with hidden text for icons-only badges
<Badge>
  <CheckIcon />
  <span className="sr-only">Verified</span>
</Badge>

Color Contrast

Ensure sufficient contrast ratios:

// Built-in variants maintain WCAG AA compliance
<Badge variant="default">High Contrast</Badge>

// Check custom colors
<Badge className="bg-yellow-400 text-black">Custom Color</Badge>

Interactive Badges

Provide proper focus states and keyboard navigation:

<Badge asChild>
  <button 
    onClick={handleClick}
    className="focus:outline-none focus:ring-2 focus:ring-primary"
    aria-label="Remove tag"
  >
    Remove <XIcon />
  </button>
</Badge>

Performance Considerations

Bundle Size

Badge components are lightweight but consider:

// Import only what you need from icon libraries
import { CheckIcon } from 'lucide-react'

// Not the entire library
import * as Icons from 'lucide-react' // ❌

Rendering Optimization

For large lists of badges:

// Memoize badge components in lists
const BadgeItem = memo(({ item }) => (
  <Badge key={item.id}>{item.name}</Badge>
))

// Use keys properly for React reconciliation
{items.map(item => (
  <Badge key={item.id}>{item.name}</Badge>
))}

Troubleshooting

Common Issues

  1. Badge not showing: Check if content is provided
  2. Icons not aligned: Ensure proper icon size (use size-3 class)
  3. Interactive badges not responding: Verify event handlers and focus states
  4. Inconsistent spacing: Use consistent gap classes in containers

Debugging

// Add visual debugging
<Badge className="border-2 border-red-500 bg-red-100">
  Debug Badge
</Badge>

// Check variant application
console.log('Badge variant:', variant) // Log current variant

Integration Examples

With Forms

function FormWithBadges() {
  const [selectedTags, setSelectedTags] = useState<string[]>([])
  
  return (
    <form className="space-y-4">
      <div>
        <label>Selected Categories</label>
        <div className="flex flex-wrap gap-2 mt-2">
          {selectedTags.map(tag => (
            <Badge key={tag} variant="secondary">
              {tag}
              <button onClick={() => removeTag(tag)}>
                <XIcon className="size-3" />
              </button>
            </Badge>
          ))}
        </div>
      </div>
    </form>
  )
}

With Data Tables

function DataTableWithBadges({ data }) {
  return (
    <table>
      {data.map(row => (
        <tr key={row.id}>
          <td>{row.name}</td>
          <td>
            <Badge variant={row.status === 'active' ? 'default' : 'secondary'}>
              {row.status}
            </Badge>
          </td>
        </tr>
      ))}
    </table>
  )
}

SEO Considerations

  1. Semantic HTML: Badges maintain proper semantic structure
  2. Content Indexing: Badge text content is indexed by search engines
  3. Structured Data: Consider schema markup for product badges
  4. Performance: Lightweight component doesn't impact Core Web Vitals
  5. Accessibility: Proper ARIA attributes improve SEO rankings