Badge
A <Badge> is a small inline label. It flags status, count, or category next to a noun — never a verb. Six variants carry meaning; never pick a variant for color alone.
When to use
- Inline status next to a name (
Acme Corp [Active]). - Counts inline with a label (
Inbox 12). - Tag chips on cards or list rows.
Don’t use for actions. A clickable badge is a button or a link — use those primitives. Badges are nouns; if you find yourself wanting onClick, you’ve reached for the wrong thing.
Variants
<Badge>Default</Badge><Badge variant="secondary">Secondary</Badge><Badge variant="outline">Outline</Badge><Badge variant="ghost">Ghost</Badge><Badge variant="destructive">Destructive</Badge><Badge variant="link">Link</Badge>| Variant | Use |
|---|---|
default | Active / primary status. Use sparingly — one per row. |
secondary | Neutral status that still earns a chip (e.g., Draft). |
outline | Categorical tag where the chip shouldn’t compete visually. |
ghost | Read-only metadata that lives quietly next to a noun. |
destructive | Failure / blocked / archived. Pair with the verb in adjacent text. |
link | Inside body text where the badge is the link target. |
For named entity statuses (DRAFT, PUBLISHED, ARCHIVED, …) use StatusBadge instead — it owns the status-to-variant map.
With icon
A leading icon reinforces meaning. Don’t add an icon if the label alone is unambiguous (Active, Draft).
<Badge><IconCheck />Verified</Badge><Badge variant="secondary"><IconClock />Pending</Badge><Badge variant="destructive"><IconAlertTriangle />Failed</Badge>As a link
Pass asChild and wrap an <a>. Use variant="link" when the badge is the link.
<Badge asChild variant="link"> <a href="/path">View →</a></Badge>API
| Prop | Type | Default | Description |
|---|---|---|---|
variant | "default" | "secondary" | "outline" | "ghost" | "destructive" | "link" | "default" | Visual + semantic variant. |
asChild | boolean | false | Render the styling onto the child element (Radix Slot). Use to render a Badge as an <a> for navigation. |
className | string | — | Forwarded — use sparingly; prefer variants. |
...rest | HTMLAttributes<HTMLSpanElement> | — | All native span attributes are forwarded. |
Design guidelines
✓ Do
- Use one default-variant badge per row. The eye should land on it once.
- Lead with the noun: "Active", "Draft", "Archived". Capitalize like a state machine.
- For named entity statuses, reach for StatusBadge — it standardizes the variant per status.
✗ Don't
- Use destructive for non-error states to "draw attention". The system has only one red.
- Add onClick — that is a Button. Wrap an <a> via asChild for nav.
- Use a Badge to display a number larger than 99. That is a count, not a chip.
Accessibility
- Default semantic is a
<span>. Inherits surrounding context — no role needed. - For dynamic values (counts that update, status changes), wrap the surrounding region in
aria-live="polite"on the parent, not the Badge itself. - Color is never the only signal — every variant pairs with a label that carries the meaning.
Related
StatusBadge— Named entity statuses with the variant map baked in. Use this 90% of the time.Button— Use when the user’s expected action is to click.Tooltip— Wrap a Badge to add an on-hover explanation.