Badge
betaA small label for a status, category, or count, with tones, emphasis levels, and sizes.
Preview
Install
1. Add the registry to
components.json, once per app. Without it, the shadcn CLI looks up@txbrendanin a public list that txbrendan/ui does not control.npx shadcn@latest registry add "@txbrendan=https://txbrendan.dev/r/{name}.json"2. Add the theme, once per app. It replaces your color tokens with ones checked for contrast.
npx shadcn@latest add @txbrendan/theme3. Add the component.
npx shadcn@latest add @txbrendan/badge
Use it for
- Labeling a status or category next to other content: Paid, Draft, Beta, Failed.
- Showing a short count next to a label, like unread messages.
- Use tone to say what the label means and emphasis to say how much it should stand out.
- Use soft or outline in tables and long lists. Save solid for the one label that must stand out.
Avoid it for
- Anything people click. A badge is not focusable. Use a Button with size='sm', or a link styled with badgeVariants().
- Sentences or long text. A badge never wraps. Use an Alert or plain text.
- Showing meaning with color alone. The text must say the status, like 'Failed', not just turn red.
- Using tone='danger' for anything that is not an error or a risk.
Props
- tone
Color role.
"neutral" | "brand" | "danger" | "success" | "warning" · default "neutral"
- emphasis
How loud the badge is.
"solid" | "soft" | "outline" | "ghost" · default "solid"
- size
Height, padding, and text size. Density scales them.
"sm" | "md" | "lg" · default "md"
- className
Extra classes. Later classes win, so className='rounded-full' replaces the radius.
string
| Prop | Type | Default | What it does |
|---|---|---|---|
| tone | "neutral" | "brand" | "danger" | "success" | "warning" | "neutral" | Color role. |
| emphasis | "solid" | "soft" | "outline" | "ghost" | "solid" | How loud the badge is. |
| size | "sm" | "md" | "lg" | "md" | Height, padding, and text size. Density scales them. |
| className | string | - | Extra classes. Later classes win, so className='rounded-full' replaces the radius. |
Accessibility
- Renders a plain <span> with no role, so screen readers read its text in line with the content around it.
- It is not focusable and does nothing on click. Keyboard users skip past it.
- The text must carry the meaning. Some people cannot tell the tones apart.
- A badge with only an icon needs words for screen readers. Add <span className="sr-only">Online</span> inside it.
- The theme tests hold badge text to 4.5:1: solid and soft text on their own fills, and outline and ghost text on the page background.
- Screen readers do not hear a count change. If the change matters, announce it in a role="status" region.
Examples
Status in a table row
<Badge tone="success" emphasis="soft">Paid</Badge>Count next to a label
<span className="inline-flex items-center gap-2">
Inbox
<Badge tone="brand" size="sm">
12<span className="sr-only"> unread</span>
</Badge>
</span>With an icon
import { CircleCheck } from "lucide-react";
<Badge tone="success" emphasis="outline">
<CircleCheck />
Verified
</Badge>A link that looks like a badge
import Link from "next/link";
import { badgeVariants } from "@/components/ui/badge";
<Link href="/tags/react" className={badgeVariants({ emphasis: "outline" })}>
react
</Link>