Skip to content

Badge

Stable

A short, non-interactive label that tags an object with a status, category or count.

OperationalNewv1.0.0Draft

Anatomy

OperationalDocs
  1. 1DotOptional. A status dot in the badge's color that can pulse for ongoing states.
  2. 2ContainerA small pill. Semantic variants tint it with the status colors; outline adds a border instead of a fill.
  3. 3LabelA word or two in sentence case: a status, a count or a category.
  4. 4IconOptional. Sized by the badge, which tightens its padding on that side.

Installation

$ pnpm dlx shadcn@latest add https://www.prfct.dev/r/badge.json

Usage

import { Badge, BadgeDot } from "@/components/ui/badge"
<Badge variant="success">
  <BadgeDot />
  Paid
</Badge>

Examples

Variants

Variants map to meaning, not to taste. The four status variants — success, warning, destructive, info — are reserved for state, so a green badge always means the same thing everywhere in a product.

DefaultSecondaryOutlineBrandSuccessWarningDestructiveInfoGhostLink
VariantUse it for
defaultHigh-emphasis labels that must stand out on busy surfaces. Use sparingly.
secondaryNeutral metadata: versions, file types, roles, counts.
outlineNeutral categories and tags that should recede.
brandHighlights such as New or Beta, and the one featured option in a set.
successCompleted, healthy or approved states: Paid, Operational, Merged.
warningStates that need attention soon: Pending, Degraded, Expiring.
destructiveFailures and blocking states: Failed, Outage, Overdue.
infoNeutral, informative states: Maintenance, Scheduled, Draft review.
ghost, linkBadges inside dense rows or text that should look like metadata until hovered.

Sizes

default (20px) fits alongside body text and in table cells. Use sm for counters inside navigation and lg for badges that sit next to headings.

SmallDefaultLarge

Status

Pair status badges with BadgeDot. The dot makes a column of states scannable at a glance; the word makes the state unambiguous. Set live to pulse the dot for states that are actively changing — the pulse stops automatically when reduced motion is on.

  • APIOperational
  • DashboardDegraded
  • WebhooksOutage
  • Search indexMaintenance

With icons

Mark icons with data-icon="inline-start" or data-icon="inline-end"; the badge tightens its padding on that side and sizes the icon to 12px.

VerifiedAI generatedPrivate+12.4%Changelog

A badge can navigate — to a changelog entry, a filtered list, a tag. Use the render prop so it becomes a real link; link badges pick up a hover state automatically.

<Badge variant="brand" render={<Link href="/docs/resources/changelog" />}>
  prfct 1.0 is here
</Badge>

Counts

Counts label a destination with how much is waiting there. Cap them at 99+, use the solid default variant only for unread items that need action, and give the number context for screen readers.

Guidelines

When to use

  • To show the state of an object: a payment, a deployment, a service.
  • To add short metadata: a role, a version, a plan, a tag.
  • To show a count of items waiting behind a navigation item.

When not to use

  • To trigger an action — use a Button.
  • To filter a view — use a Toggle Group or checkboxes, so the selected state is announced.
  • To explain something — if it needs more than two words, it needs a sentence, an Alert or a Tooltip.

Never color alone

PaidOverdue
Do.A dot for scanning and a word for meaning. Readable in grayscale, for color-blind people and by screen readers.
Don’t.A colored dot or chip with no label forces people to memorize a color code.

Keep it short

Expiring soon
Do.One or two words, sentence case, no punctuation.
Your card expires in 3 days, update it
Don’t.Sentences in badges truncate, wrap awkwardly and can't be scanned.

Restraint

Badges work because they are rare. If every row in a table carries three badges, none of them is noticed — keep one status badge per object and move the rest of the metadata into plain text.

Accessibility

  • Static by default. A badge renders a <span> and is not focusable. When it navigates, render it as a link so it gets a role, focus and a visible focus ring.
  • Status is text. Every status variant pairs a color with a word. BadgeDot is aria-hidden; the label carries the meaning.
  • Counts need context. "12" alone is ambiguous when read aloud. Add a visually hidden suffix — <span className="sr-only"> unread</span> — or put the count in the link's accessible name.
  • Contrast. Soft variants use step 11 text on step 3 fills, which the palette guarantees at 4.5:1 or more in both modes. Solid variants meet 4.5:1 as well.
  • Motion. The live pulse is decorative and is disabled under prefers-reduced-motion.

API reference

Badge

Renders a <span>. Built on Base UI's useRender, so it accepts a render prop.

PropTypeDefault
variant

Meaning and emphasis of the badge.

"default" | "secondary" | "outline" | "brand" | "success" | "warning" | "destructive" | "info" | "ghost" | "link""default"
size

18, 20 or 24 pixels tall.

"sm" | "default" | "lg""default"
render

Renders a different element, such as a link, while keeping the badge styles.

ReactElement | (props, state) => ReactElementNo default

BadgeDot

A 6px status dot that takes the badge's text color.

PropTypeDefault
live

Adds a pulsing ring for states that are actively changing. Disabled under reduced motion.

booleanfalse

badgeVariants

The class generator is exported so other elements — a table cell, a custom link — can share badge styles.

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

<span className={badgeVariants({ variant: "info", size: "sm" })}>Beta</span>