Dependencies
tailwindcssshadcn/uilucide-react
shadcn/ui components
npx shadcn@latest add badge

shadcn/ui Badge is a small, flexible component for labeling and categorizing content. These 4 examples cover all common badge patterns.

Installation

npx shadcn@latest add badge

Usage

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

<Badge>Default</Badge>
<Badge variant="secondary">Secondary</Badge>
<Badge variant="destructive">Error</Badge>
<Badge variant="outline">Outline</Badge>

1. shadcn/ui Badge Variants

All shadcn/ui badge variants — default, secondary, destructive and outline.

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

export default function BadgeVariants() {
  return (
    <div className="space-y-4">
      {/* shadcn variants */}
      <div>
        <p className="text-sm text-muted-foreground mb-2">shadcn/ui variants</p>
        <div className="flex flex-wrap gap-2">
          <Badge>Default</Badge>
          <Badge variant="secondary">Secondary</Badge>
          <Badge variant="destructive">Destructive</Badge>
          <Badge variant="outline">Outline</Badge>
        </div>
      </div>

      {/* Custom color badges */}
      <div>
        <p className="text-sm text-muted-foreground mb-2">Custom Tailwind colors</p>
        <div className="flex flex-wrap gap-2">
          <span className="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-semibold text-white" style={{ background: "#fd4766" }}>Brand</span>
          <span className="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-semibold bg-green-100 text-green-700">Success</span>
          <span className="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-semibold bg-yellow-100 text-yellow-700">Warning</span>
          <span className="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-semibold bg-blue-100 text-blue-700">Info</span>
          <span className="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-semibold bg-gray-100 text-gray-600">Neutral</span>
          <span className="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-semibold bg-purple-100 text-purple-700">Premium</span>
        </div>
      </div>

      {/* Pill vs rounded */}
      <div>
        <p className="text-sm text-muted-foreground mb-2">Shape variants</p>
        <div className="flex flex-wrap gap-2 items-center">
          <span className="inline-flex px-2.5 py-0.5 rounded text-xs font-semibold bg-gray-100 text-gray-700">Square-ish</span>
          <span className="inline-flex px-2.5 py-0.5 rounded-full text-xs font-semibold bg-gray-100 text-gray-700">Pill</span>
          <span className="inline-flex px-1.5 py-0.5 rounded text-xs font-bold text-white" style={{ background: "#fd4766", minWidth: "20px", justifyContent: "center" }}>3</span>
        </div>
      </div>
    </div>
  )
}

2. React Status Badges

Status indicator badges with colored dots — common in user lists, order tables and dashboards.

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

type Status = "active" | "inactive" | "pending" | "draft" | "archived"

const statusConfig: Record<Status, { label: string; dot: string; bg: string; text: string }> = {
  active:   { label: "Active",   dot: "#10b981", bg: "bg-green-50",  text: "text-green-700" },
  inactive: { label: "Inactive", dot: "#6b7280", bg: "bg-gray-50",   text: "text-gray-600"  },
  pending:  { label: "Pending",  dot: "#f59e0b", bg: "bg-yellow-50", text: "text-yellow-700"},
  draft:    { label: "Draft",    dot: "#4f6ef7", bg: "bg-blue-50",   text: "text-blue-700"  },
  archived: { label: "Archived", dot: "#ef4444", bg: "bg-red-50",    text: "text-red-700"   },
}

function StatusBadge({ status }: { status: Status }) {
  const config = statusConfig[status]
  return (
    <span className={`inline-flex items-center gap-1.5 px-2.5 py-1 rounded-full text-xs font-semibold ${config.bg} ${config.text}`}>
      <span className="w-1.5 h-1.5 rounded-full" style={{ background: config.dot }} />
      {config.label}
    </span>
  )
}

const templates = [
  { name: "Marvel Dashboard", framework: "Angular 21", status: "active" as Status },
  { name: "PORTO Template",   framework: "Bootstrap 5", status: "active" as Status },
  { name: "Proctu Medical",   framework: "React 19",    status: "pending" as Status },
  { name: "Kiosk Dashboard",  framework: "Next.js 15",  status: "draft" as Status },
  { name: "CANVA UI Kit",     framework: "Bootstrap 5", status: "archived" as Status },
]

export default function StatusBadges() {
  return (
    <div className="space-y-4">
      {/* Status legend */}
      <div className="flex flex-wrap gap-2">
        {(Object.keys(statusConfig) as Status[]).map((s) => (
          <StatusBadge key={s} status={s} />
        ))}
      </div>

      {/* In a table */}
      <div className="rounded-lg border overflow-hidden">
        <table className="w-full text-sm">
          <thead className="bg-gray-50">
            <tr>
              <th className="text-left px-4 py-2.5 font-semibold text-gray-600">Template</th>
              <th className="text-left px-4 py-2.5 font-semibold text-gray-600">Framework</th>
              <th className="text-left px-4 py-2.5 font-semibold text-gray-600">Status</th>
            </tr>
          </thead>
          <tbody className="divide-y">
            {templates.map((t) => (
              <tr key={t.name} className="hover:bg-gray-50">
                <td className="px-4 py-3 font-medium">{t.name}</td>
                <td className="px-4 py-3 text-gray-500">{t.framework}</td>
                <td className="px-4 py-3"><StatusBadge status={t.status} /></td>
              </tr>
            ))}
          </tbody>
        </table>
      </div>
    </div>
  )
}

3. React Notification Count Badges

Notification badges overlaid on buttons and icons using absolute positioning.

tsx
import { Button } from "@/components/ui/button"
import { Bell, MessageSquare, ShoppingCart, Mail } from "lucide-react"

export default function NotificationBadges() {
  return (
    <div className="space-y-6">
      {/* Icon buttons with notification counts */}
      <div>
        <p className="text-sm text-muted-foreground mb-3">Icon notification badges</p>
        <div className="flex items-center gap-4">
          <button className="relative p-2 text-gray-600 hover:bg-gray-100 rounded-lg">
            <Bell size={22} />
            <span className="absolute -top-1 -right-1 w-5 h-5 rounded-full text-white text-xs font-bold flex items-center justify-center" style={{ background: "#fd4766", fontSize: "0.6rem" }}>4</span>
          </button>
          <button className="relative p-2 text-gray-600 hover:bg-gray-100 rounded-lg">
            <MessageSquare size={22} />
            <span className="absolute -top-1 -right-1 w-5 h-5 rounded-full text-white text-xs font-bold flex items-center justify-center" style={{ background: "#fd4766", fontSize: "0.6rem" }}>12</span>
          </button>
          <button className="relative p-2 text-gray-600 hover:bg-gray-100 rounded-lg">
            <ShoppingCart size={22} />
            <span className="absolute -top-1 -right-1 w-5 h-5 rounded-full text-white text-xs font-bold flex items-center justify-center" style={{ background: "#10b981", fontSize: "0.6rem" }}>3</span>
          </button>
          <button className="relative p-2 text-gray-600 hover:bg-gray-100 rounded-lg">
            <Mail size={22} />
            <span className="absolute -top-1 -right-1 w-4 h-4 rounded-full" style={{ background: "#fd4766" }} />
          </button>
        </div>
      </div>

      {/* Button with badge */}
      <div>
        <p className="text-sm text-muted-foreground mb-3">Buttons with badges</p>
        <div className="flex flex-wrap gap-3">
          <Button variant="outline" className="relative gap-2">
            <Bell size={15} />
            Notifications
            <span className="ml-1 px-1.5 py-0.5 rounded-full text-white text-xs font-bold" style={{ background: "#fd4766", fontSize: "0.65rem" }}>4</span>
          </Button>
          <Button variant="outline" className="relative gap-2">
            <MessageSquare size={15} />
            Messages
            <span className="ml-1 px-1.5 py-0.5 rounded-full text-white text-xs font-bold" style={{ background: "#4f6ef7", fontSize: "0.65rem" }}>99+</span>
          </Button>
        </div>
      </div>

      {/* Nav item with badge */}
      <div>
        <p className="text-sm text-muted-foreground mb-3">Sidebar nav item with badge</p>
        <div className="w-48 bg-white border rounded-lg p-2">
          {[
            { label: "Dashboard", count: null },
            { label: "Orders", count: 12 },
            { label: "Messages", count: 4 },
            { label: "Reports", count: null },
          ].map((item) => (
            <div key={item.label} className="flex items-center justify-between px-3 py-2 rounded-md hover:bg-gray-50 text-sm cursor-pointer">
              <span className="text-gray-700">{item.label}</span>
              {item.count && (
                <span className="text-white text-xs px-1.5 py-0.5 rounded-full" style={{ background: "#fd4766", fontSize: "0.62rem" }}>
                  {item.count}
                </span>
              )}
            </div>
          ))}
        </div>
      </div>
    </div>
  )
}

4. React Tag Badges

Removable tag badges for filtering, categorization and multi-select inputs.

tsx
import { useState } from "react"
import { X } from "lucide-react"

const initialTags = ["Angular 21", "Bootstrap 5", "React 19", "Next.js 15", "TypeScript", "Tailwind CSS"]

const frameworkColors: Record<string, { bg: string; text: string }> = {
  "Angular 21":   { bg: "bg-red-100",    text: "text-red-700"    },
  "Bootstrap 5":  { bg: "bg-purple-100", text: "text-purple-700" },
  "React 19":     { bg: "bg-blue-100",   text: "text-blue-700"   },
  "Next.js 15":   { bg: "bg-gray-100",   text: "text-gray-700"   },
  "TypeScript":   { bg: "bg-sky-100",    text: "text-sky-700"    },
  "Tailwind CSS": { bg: "bg-teal-100",   text: "text-teal-700"   },
}

export default function TagBadges() {
  const [tags, setTags] = useState(initialTags)
  const [input, setInput] = useState("")

  const removeTag = (tag: string) => setTags(tags.filter((t) => t !== tag))

  const addTag = (e: React.KeyboardEvent) => {
    if (e.key === "Enter" && input.trim() && !tags.includes(input.trim())) {
      setTags([...tags, input.trim()])
      setInput("")
    }
  }

  return (
    <div className="space-y-4 max-w-md">
      {/* Removable tags */}
      <div>
        <p className="text-sm font-semibold mb-2">Technology Tags</p>
        <div className="flex flex-wrap gap-2">
          {tags.map((tag) => {
            const colors = frameworkColors[tag] || { bg: "bg-gray-100", text: "text-gray-700" }
            return (
              <span
                key={tag}
                className={`inline-flex items-center gap-1 px-2.5 py-1 rounded-full text-xs font-semibold ${colors.bg} ${colors.text}`}
              >
                {tag}
                <button
                  onClick={() => removeTag(tag)}
                  className="ml-0.5 hover:opacity-70 transition-opacity"
                >
                  <X size={11} />
                </button>
              </span>
            )
          })}
        </div>
      </div>

      {/* Add tag input */}
      <div>
        <p className="text-sm text-muted-foreground mb-2">Press Enter to add a tag</p>
        <input
          type="text"
          value={input}
          onChange={(e) => setInput(e.target.value)}
          onKeyDown={addTag}
          placeholder="Add a tag..."
          className="w-full border rounded-md px-3 py-2 text-sm focus:outline-none focus:ring-2"
          style={{ "--tw-ring-color": "#fd4766" } as React.CSSProperties}
        />
      </div>

      {/* Static category tags */}
      <div>
        <p className="text-sm font-semibold mb-2">Category Filter</p>
        <div className="flex flex-wrap gap-2">
          {["All", "Navbar", "Cards", "Forms", "Tables", "Modals"].map((cat, i) => (
            <button
              key={cat}
              className="px-3 py-1 rounded-full text-xs font-semibold transition-all"
              style={
                i === 0
                  ? { background: "#fd4766", color: "#fff" }
                  : { background: "#f3f4f6", color: "#4b5563" }
              }
            >
              {cat}
            </button>
          ))}
        </div>
      </div>
    </div>
  )
}

Frequently Asked Questions

Run npx shadcn@latest add badge. This creates components/ui/badge.tsx in your project. Import with: import { Badge } from '@/components/ui/badge'.
Use className to override: <Badge className='bg-green-100 text-green-700 hover:bg-green-200'>. Or pass inline style for brand colors: <Badge style={{ background: '#fd4766', color: '#fff' }}>. For reusable custom variants edit badge.tsx directly.
Wrap the button in a relative div, then add the badge with position absolute: <div className='relative'><button>...</button><span className='absolute -top-1 -right-1 w-5 h-5 rounded-full bg-red-500 text-white text-xs flex items-center justify-center'>4</span></div>.

Need a Full React + Next.js Dashboard Template?

Get a complete React + Next.js dashboard with 50+ components — built by the same team behind BootstrapPlanet.

Browse Templates →

Use code FIRST30 for 30% off.

Related Components