Count badge
FreeA count on an icon or tab that rolls up or down to its new number, pops once when something new arrives and shrinks away at zero.
pnpm dlx shadcn@latest add https://caliperui.site/r/count-badge.jsonInbox, 3 unreadAlerts, 98 alerts
When to use #
Use it for
- Unread messages, notifications and items to review.
- Cart and queue sizes.
Skip it when
- Totals people read rather than glance at; write the number out.
Installation #
terminal
pnpm dlx shadcn@latest add https://caliperui.site/r/count-badge.json
Adds its 2 files to components/ui/, each with its own stylesheet, and installs their packages. No account needed. The first part also adds Caliper UI’s tokens; import them once as in Installation.
Usage #
This is the code behind the preview above.
example.tsx
import { Bell, Inbox } from "lucide-react" import { useState } from "react" import { Button } from "@/components/ui/button" import { CountBadge } from "@/components/ui/count-badge" export function CountBadgeDemo() { const [unread, setUnread] = useState(3); const [alerts, setAlerts] = useState(98); return ( <div className="grid justify-items-center gap-5"> <div className="flex items-center gap-8"> <CountBadge count={unread} noun="unread"> <span className="grid size-10 place-items-center rounded-[12px] bg-muted"><Inbox aria-hidden="true" className="size-5" /><span className="sr-only">Inbox</span></span> </CountBadge> <CountBadge count={alerts} noun="alerts"> <span className="grid size-10 place-items-center rounded-[12px] bg-muted"><Bell aria-hidden="true" className="size-5" /><span className="sr-only">Alerts</span></span> </CountBadge> </div> <div className="flex flex-wrap justify-center gap-2"> <Button size="sm" variant="secondary" onClick={() => setUnread((n) => n + 1)}>New message</Button> <Button size="sm" variant="secondary" onClick={() => setUnread((n) => Math.max(0, n - 1))}>Read one</Button> <Button size="sm" variant="ghost" onClick={() => setAlerts((n) => n + 1)}>Another alert</Button> </div> </div> ); }
API reference #
CountBadge
component
Also accepts the native props of its root element and a ref.
PropTypeDefaultDescription
count *number—
maxnumber99
nounstring"new"Read after the number, e.g. "unread": "3 unread".
childrenReactNode—What the count sits on: an icon or a label.
Accessibility #
It is built on native elements and keeps aria-hidden in step with state.
No custom key handling; Tab and the native activation keys do the work.
Motion #
It declares no animation in its component source. Any transition comes from its stylesheet, which uses the duration tokens that collapse to instant under prefers-reduced-motion.