Notification Badge
A counter or dot on an icon or avatar, cut out with CSS shape() so it reads on any background
Installation
pnpm dlx shadcn@latest add @alexandrejoly/notification-badgeUsage
import { NotificationBadge, NotificationBadgeContent, NotificationBadgeIndicator,} from "@/components/ui/notification-badge";<NotificationBadge value={3}> <NotificationBadgeContent> <Button aria-label="Notifications, 3 unread" size="icon" variant="outline"> <BellIcon /> </Button> </NotificationBadgeContent> <NotificationBadgeIndicator /></NotificationBadge>Examples
Dot
A dot signals that something is new, without a number.
Counts
One character makes a circle, more make a pill, and the hole under it, the transparent cut-out around the badge, keeps the same shape. The demo caps the count at 99+ itself, since value shows whatever it gets.
Compact counts
value takes a string, so Intl.NumberFormat with notation: "compact" can shorten a large count to 1.2K. The demo pins "en" so the server and the browser format alike; pass the page’s locale instead. The label keeps the full number.
Beside a label
Beside a label, NotificationBadgeContent is left out and static takes the indicator out of the overlay.
Accessibility
The indicator is aria-hidden, since its count, read alone, doesn’t say what it counts. Put the count in the name of the button instead.
On an icon button, write it in the aria-label: “Notifications, 3 unread”, or “Messages, new activity” for a dot, which shows no count at all. Beside a label, keep the visible text first and add the rest in an sr-only span, so the name still starts with what the button shows.
Build the whole phrase with your translation library rather than joining pieces, and say “more than 99” where you show 99+.
A count that changes is not announced. To announce it, update one polite live region for the whole app with a phrase that says what changed, such as “2 new messages”.
In forced colors mode, such as Windows high contrast, the badge takes the system text color, so even a dot stays visible.
API reference
NotificationBadge
Shares the value with its parts. Sets the geometry of the hole, the transparent cut-out around the badge, as CSS variables.
Takes every prop of span.
| Prop or variable | Type | Default |
|---|---|---|
valueWhat the badge shows, as is: format it yourself, as | number | string | None |
variantA counter, or a dot shown while Default: | "default" | "dot" | "default" |
--hole-radiusThe corner radius of the content, so the badge sits on its curve: Default: | CSS length | 0px |
--hole-gapThe transparent gap around the badge. Default: | CSS length | 1.5px |
--hole-top, --hole-rightMoves the badge and its hole together, from the top and right edges. | CSS length | None |
NotificationBadgeContent
Wraps the icon, button or avatar. Clips a hole under the badge so the gap shows whatever is behind it.
Takes every prop of span.
NotificationBadgeIndicator
A badge in the destructive color: a circle for one character, a pill for more, sized and placed from the root’s variables. Renders nothing while value is 0 or below, or empty, and is aria-hidden: see Accessibility. Add static to its class to sit it beside a label instead of over the content.
Takes every prop of span.