Badge
A badge or a component that looks like a badge. Built with Base UI and Tailwind CSS. Copy-paste ready.
import { Badge } from "@/components/ui/badge";
export default function Particle() {
return <Badge>Badge</Badge>;
}
Installation
pnpm dlx shadcn@latest add @cnippet/badge
Usage
import { Badge } from "@/components/ui/badge"<Badge>Badge</Badge>Link
You can use the render prop to make another component look like a badge. Here's an example of a link that looks like a badge.
import Link from "next/link"
import { Badge } from "@/components/ui/badge"
export function LinkAsBadge() {
return <Badge render={<Link href="/pricing" />}>New</Badge>
}Examples
Outline
A bordered badge with a transparent background — use for secondary or neutral status labels.
import { Badge } from "@/components/ui/badge";
export default function Particle() {
return <Badge variant="outline">Badge</Badge>;
}
Secondary
A lower-contrast badge for supplementary metadata that shouldn't compete with primary content.
import { Badge } from "@/components/ui/badge";
export default function Particle() {
return <Badge variant="secondary">Badge</Badge>;
}
Destructive
A red badge for error states, failed operations, or items requiring immediate attention.
import { Badge } from "@/components/ui/badge";
export default function Particle() {
return <Badge variant="destructive">Badge</Badge>;
}
Info
A blue badge for informational or neutral status indicators.
import { Badge } from "@/components/ui/badge";
export default function Particle() {
return <Badge variant="info">Badge</Badge>;
}
Success
A green badge to confirm a completed action or a healthy status.
import { Badge } from "@/components/ui/badge";
export default function Particle() {
return <Badge variant="success">Badge</Badge>;
}
Warning
An amber badge to flag a potential issue that requires attention but is not yet blocking.
import { Badge } from "@/components/ui/badge";
export default function Particle() {
return <Badge variant="warning">Badge</Badge>;
}
Error
Distinct from destructive — use the error variant for validation errors or system fault states.
import { Badge } from "@/components/ui/badge";
export default function Particle() {
return <Badge variant="error">Badge</Badge>;
}
Size Variations
Renders sm, default, and lg size options to show how the badge scales across different contexts.
import { Badge } from "@/components/ui/badge";
export default function Particle() {
return (
<div className="flex items-center gap-2.5">
<Badge size="sm">Small</Badge>
<Badge size="default">Default</Badge>
<Badge size="lg">Large</Badge>
</div>
);
}
With Icon
A leading icon reinforces the badge's meaning — common for status badges like "Verified", "Live", or "Error".
import { CheckIcon } from "lucide-react";
import { Badge } from "@/components/ui/badge";
export default function Particle() {
return (
<Badge variant="outline">
<CheckIcon aria-hidden="true" />
Verified
</Badge>
);
}
With Link
Renders the badge as an anchor element using the render prop, so the entire badge is a clickable navigation target.
import Link from "next/link";
import { Badge } from "@/components/ui/badge";
export default function Particle() {
return <Badge render={<Link href="/" />}>Badge</Badge>;
}
With Count
A numeric badge used to show unread counts, pending items, or notification numbers alongside a label.
import { Badge } from "@/components/ui/badge";
export default function Particle() {
return <Badge className="rounded-full">7</Badge>;
}
Status Dot Indicators
Online, Busy, Away, and Offline statuses each rendered with a small colored dot inside a semantically-colored badge, ideal for presence indicators in user lists.
import { Badge } from "@/components/ui/badge";
type Status = {
label: string;
dotClass: string;
variant:
| "default"
| "outline"
| "secondary"
| "success"
| "warning"
| "destructive"
| "info";
};
const statuses: Status[] = [
{ dotClass: "bg-emerald-500", label: "Online", variant: "success" },
{ dotClass: "bg-red-500", label: "Busy", variant: "destructive" },
{ dotClass: "bg-yellow-400", label: "Away", variant: "warning" },
{ dotClass: "bg-slate-400", label: "Offline", variant: "secondary" },
];
export default function Particle() {
return (
<div className="flex flex-wrap items-center gap-2">
{statuses.map((s) => (
<Badge key={s.label} variant={s.variant}>
<span
aria-hidden="true"
className={`size-1.5 rounded-full ${s.dotClass}`}
/>
{s.label}
</Badge>
))}
</div>
);
}
Release Channel Tags
A set of four release channel labels — Stable, Beta, Experimental, and Deprecated — each using the appropriate semantic variant to communicate maturity level at a glance.
Release channels
import { Badge } from "@/components/ui/badge";
const channels = [
{ label: "Stable", variant: "success" as const },
{ label: "Beta", variant: "info" as const },
{ label: "Experimental", variant: "warning" as const },
{ label: "Deprecated", variant: "destructive" as const },
];
export default function Particle() {
return (
<div className="flex flex-col gap-3">
<p className="text-muted-foreground text-sm">Release channels</p>
<div className="flex flex-wrap items-center gap-2">
{channels.map((c) => (
<Badge key={c.label} variant={c.variant}>
{c.label}
</Badge>
))}
</div>
</div>
);
}
Plan Tier Badges
Subscription tiers (Free, Starter, Pro, Enterprise) displayed as badge-price rows inside a Frame, useful for plan comparison sections or upgrade prompts.
import { Badge } from "@/components/ui/badge";
import { Frame, FramePanel } from "@/components/ui/frame";
const tiers = [
{
description: "For individuals getting started",
label: "Free",
price: "$0/mo",
variant: "secondary" as const,
},
{
description: "For small teams and projects",
label: "Starter",
price: "$12/mo",
variant: "info" as const,
},
{
description: "Advanced features for growing teams",
label: "Pro",
price: "$49/mo",
variant: "default" as const,
},
{
description: "Custom pricing for large organisations",
label: "Enterprise",
price: "Custom",
variant: "success" as const,
},
];
export function Pattern() {
return (
<div className="mx-auto w-full max-w-sm">
<Frame>
{tiers.map((tier) => (
<FramePanel
className="flex items-center justify-between gap-3"
key={tier.label}
>
<div className="flex items-center gap-2.5">
<Badge size="sm" variant={tier.variant}>
{tier.label}
</Badge>
<span className="text-muted-foreground text-sm">
{tier.description}
</span>
</div>
<span className="shrink-0 font-semibold text-sm">{tier.price}</span>
</FramePanel>
))}
</Frame>
</div>
);
}
Notification Categories
A sidebar-style list of notification types (Messages, Mentions, Reviews) where each row uses a ghost button with a trailing badge showing the unread count.
Notifications
import { BellIcon, GitPullRequestIcon, MessageSquareIcon } from "lucide-react";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Frame, FramePanel } from "@/components/ui/frame";
const notifications = [
{
count: 12,
icon: MessageSquareIcon,
label: "Messages",
variant: "default" as const,
},
{
count: 3,
icon: BellIcon,
label: "Mentions",
variant: "destructive" as const,
},
{
count: 7,
icon: GitPullRequestIcon,
label: "Reviews",
variant: "info" as const,
},
];
export function Pattern() {
return (
<div className="mx-auto w-full max-w-xs">
<Frame>
<FramePanel>
<p className="mb-3 font-semibold text-sm">Notifications</p>
<div className="flex flex-col gap-1.5">
{notifications.map((n) => (
<Button
className="h-auto justify-between px-2 py-2"
key={n.label}
variant="ghost"
>
<span className="flex items-center gap-2 text-sm">
<n.icon className="size-4 text-muted-foreground" />
{n.label}
</span>
<Badge size="sm" variant={n.variant}>
{n.count}
</Badge>
</Button>
))}
</div>
</FramePanel>
</Frame>
</div>
);
}
Priority Labels in Task List
A task list where each row has a title, assignee, and a right-aligned priority badge (Urgent, High, Medium, Low) styled with the appropriate semantic variant.
import { Badge } from "@/components/ui/badge";
import { Frame, FramePanel } from "@/components/ui/frame";
type Priority = "urgent" | "high" | "medium" | "low";
const priorityVariant: Record<
Priority,
"destructive" | "warning" | "info" | "secondary"
> = {
high: "warning",
low: "secondary",
medium: "info",
urgent: "destructive",
};
const tasks = [
{
assignee: "Alex J.",
priority: "urgent" as Priority,
title: "Fix authentication token refresh race condition",
},
{
assignee: "Sarah C.",
priority: "high" as Priority,
title: "Migrate legacy REST endpoints to GraphQL",
},
{
assignee: "Marcus R.",
priority: "medium" as Priority,
title: "Add dark mode support to dashboard charts",
},
{
assignee: "Laura P.",
priority: "low" as Priority,
title: "Update onboarding copy for new signup flow",
},
];
export function Pattern() {
return (
<div className="mx-auto w-full max-w-lg">
<Frame>
{tasks.map((task) => (
<FramePanel
className="flex items-start justify-between gap-3"
key={task.title}
>
<div className="flex flex-1 flex-col gap-1">
<span className="font-medium text-sm">{task.title}</span>
<span className="text-muted-foreground text-xs">
{task.assignee}
</span>
</div>
<Badge
className="shrink-0 capitalize"
size="sm"
variant={priorityVariant[task.priority]}
>
{task.priority}
</Badge>
</FramePanel>
))}
</Frame>
</div>
);
}

