oks-ui

How to use

Badge

Badge anchors a small indicator (a count, a status dot) to the corner of whatever it wraps — a notification count on a bell icon, an online-status dot on an avatar. It wraps its children rather than replacing them, and animates a small bump whenever its content actually changes so a new value doesn't just silently pop in.

import { Badge } from "oks-ui";

Wrapping content

Badge wraps children and positions itself at one of four corners (placement) relative to them. content is what's shown inside the badge — a number, a short string, or any ReactNode.

<Badge content="3" color="danger">
  <BellIcon />
</Badge>

Numeric counts and the max cap

For a numeric notification count, pass max to cap the display at a fixed ceiling once the real count exceeds it — a common pattern for keeping a badge's width from growing unbounded.

<Badge content={unreadCount} max={99} color="danger">
  <BellIcon />
</Badge>
// unreadCount = 150 renders as "99+"

Plain status dots

Set isDot (typically without content) for a plain colored dot rather than a numeric/text badge — the common shape for an online/offline status indicator on an avatar.

<Badge isDot color="success" placement="bottom-end">
  <Avatar src={user.avatarUrl} />
</Badge>

Toggling visibility without unmounting

isInvisible hides the badge while keeping it mounted — reach for this over conditionally rendering the whole Badge when you want the bump animation to still play correctly the next time content changes, or when unmounting/remounting would otherwise cause a layout flash.

<Badge content={count} isInvisible={count === 0} color="danger">
  <BellIcon />
</Badge>

Shape and sizing

shape is circle or rectangle (rectangle, the default, widens naturally for multi-character content); isOneChar forces a perfectly circular badge sized for exactly one character, which reads cleaner than the default rectangle for a single digit or letter.

Accessibility

  • The badge indicator isn't automatically announced as part of its wrapped content's accessible name — pass ariaLabel when the badge conveys information that matters to a screen reader user ("3 unread notifications"), not just a numeric value floating next to an icon.
  • isDot badges in particular carry no readable content by default — if the status they represent matters (online/offline, a pending state), give them an ariaLabel describing what the dot means.
  • showOutline's ring exists for sighted separation between the badge and whatever it's layered on top of — it doesn't affect how the badge is announced.

Props reference

All available props for Badge. See the full component page for an interactive playground.

Props

children

ReactNode

Default:

Element the badge is anchored to.

content

string | number | ReactNode

Default:

Badge content; omit with isDot for a plain dot.

max

number

Default:

Caps a numeric content at max+ (e.g. "99+").

Best practices

  • Set max on any badge showing a genuinely unbounded count (unread messages, notifications) — an unbounded 3-4 digit badge breaks the compact layout it's meant to have.
  • Prefer isInvisible over conditionally unmounting Badge when the count can go back to zero and up again — it keeps the bump animation and positioning stable across toggles.
  • Use isDot for a binary or few-state status indicator (online/away/offline) and full content-based badges for counts — mixing the two conventions on similar UI reads inconsistently.
  • Always pair a meaningful badge with ariaLabel; don't rely on color alone (a red dot vs. a green dot) to communicate status to every user.
View full API reference for Badge