Badge
A short label for a state or a kind, tinted in its theme colour, with a dot for a state that is still changing.
import { Badge } from '@/components/panelui/badge';
export default function BadgeOverview() {
return (
<div className="flex flex-wrap items-center justify-center gap-2">
<Badge>New</Badge>
<Badge variant="secondary">Draft</Badge>
<Badge variant="outline">v2.4</Badge>
<Badge variant="success" dot>
Paid
</Badge>
<Badge variant="info" dot="pulse">
Syncing
</Badge>
<Badge variant="warning" dot>
Due Friday
</Badge>
<Badge variant="destructive" dot>
Overdue
</Badge>
</div>
);
}Use a badge to label a state (paid, overdue, building) or a kind (draft, beta, a version number) next to the thing it describes.
A badge is not a control. It cannot be pressed, toggled or removed. For a label that does any of those, use Chip.
Installation
npx shadcn@latest add https://panelui.dev/r/web/badge.jsonpnpm dlx shadcn@latest add https://panelui.dev/r/web/badge.jsonyarn dlx shadcn@latest add https://panelui.dev/r/web/badge.jsonbunx shadcn@latest add https://panelui.dev/r/web/badge.jsonThe source is copied into components/panelui/, where you can edit it.
Usage
import { Badge } from "@/components/panelui/badge"<Badge>New</Badge>
<Badge variant="success" dot>Paid</Badge>
<Badge variant="info" dot="pulse">Syncing</Badge>Examples
Statuses
success, info, warning and destructive are drawn as a tint with text in the same colour, so a column of them reads as states rather than buttons. dot="pulse" is for a state that is still changing; use a plain dot once it settles.
import { Badge } from '@/components/panelui/badge';
const RUNS = [
{ name: 'Nightly backup', status: 'Running', variant: 'info' as const, live: true },
{ name: 'Image resize queue', status: 'Healthy', variant: 'success' as const, live: false },
{ name: 'Invoice export', status: 'Retrying', variant: 'warning' as const, live: true },
{ name: 'Search reindex', status: 'Stopped', variant: 'destructive' as const, live: false },
];
export default function BadgeStatuses() {
return (
<ul className="flex w-full max-w-sm flex-col divide-y text-sm">
{RUNS.map((run) => (
<li key={run.name} className="flex items-center justify-between gap-4 py-2.5">
<span>{run.name}</span>
<Badge variant={run.variant} dot={run.live ? 'pulse' : true}>
{run.status}
</Badge>
</li>
))}
</ul>
);
}Sizes
sm, md and lg. Use sm inside a dense table row.
import { Badge } from '@/components/panelui/badge';
export default function BadgeSizes() {
return (
<div className="flex flex-wrap items-center justify-center gap-3">
<Badge size="sm" variant="secondary">
Small
</Badge>
<Badge size="md" variant="secondary">
Medium
</Badge>
<Badge size="lg" variant="secondary">
Large
</Badge>
</div>
);
}With an icon
An icon placed before the text is sized to the badge.
import { Badge } from '@/components/panelui/badge';
import { LeafIcon, LockIcon, SparklesIcon } from 'lucide-react';
export default function BadgeIcons() {
return (
<div className="flex flex-wrap items-center justify-center gap-2">
<Badge variant="outline">
<LockIcon />
Private
</Badge>
<Badge variant="success">
<LeafIcon />
Low carbon
</Badge>
<Badge>
<SparklesIcon />
Pro
</Badge>
</div>
);
}API Reference
Badge
| Prop | Type | Default | Description |
|---|---|---|---|
variant | BadgeVariant | 'default' | What it means. default is solid, for the one thing that should be seen first; the four states are tinted in their theme colour. Default default. |
size | BadgeSize | 'md' | Its height and text size. Default md. |
dot | boolean | 'pulse' | false | A dot before the text in the badge's colour. 'pulse' makes it breathe, for a state that is still changing. |
Every part also takes the props of the element it renders, and a className.
Notes
Choosing a variant
default is solid. Keep it for the one badge on a screen that should be seen first. secondary and outline are for kinds, the four colours for states.
Reduced motion
The pulse does not run when the reader has asked for reduced motion. The dot stays.