caliper uidocs

Count badge

Free

A 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.

Installs components/ui/count-badge.tsxDepends on none beyond ReactMotion none in component source
pnpm dlx shadcn@latest add https://caliperui.site/r/count-badge.json
Inbox, 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.