Skip to content

Badge component

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.

Data Display21 propsAccessibility notes

Playground

Change a prop on the right and the preview updates. Switch to Code to copy what you see.

Recipes

Copy-paste patterns for the ways Badge actually gets used — each one a real, self-contained example.

Notification count

Badge wraps a target and pins a count to its corner — the classic unread indicator on a bell or inbox icon.

<Badge content={5} color="danger">
  <BellIcon />
</Badge>

Capped count

Pass max and any higher count renders as “max+”, so a busy counter never blows out its width.

<Badge content={128} max={99} color="danger">
  <InboxIcon />
</Badge>

Status on an avatar

A small dot badge on an avatar reads as presence — pair a green dot with a user image.

<Badge content="" color="success" placement="bottom-end" size="sm">
  <Avatar src="/hero/person-woman.jpg" name="Sara" />
</Badge>

Semantic colors

Colour the badge to match meaning — danger for errors, success for done, warning for attention.

<Badge content={3} color="primary"><BellIcon /></Badge>
<Badge content={3} color="success"><BellIcon /></Badge>
<Badge content={3} color="warning"><BellIcon /></Badge>
<Badge content={3} color="danger"><BellIcon /></Badge>

Placement

Pin the badge to any corner with placement — top-end is the default; move it to suit the target.

<Badge content={1} placement="top-start"><BellIcon /></Badge>
<Badge content={1} placement="top-end"><BellIcon /></Badge>
<Badge content={1} placement="bottom-start"><BellIcon /></Badge>
<Badge content={1} placement="bottom-end"><BellIcon /></Badge>

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.

Props

The full public API for Badge. Every prop is stable and intentionally typed.

Content

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+").

Appearance

variant

"solid" | "soft" | "bordered"

default: solid

Visual style variant.

shadow

"none" | "xs" | "xs-sm" | "sm" | "md" | "lg"

default: none

Box-shadow depth.

color

"default" | "primary" | "secondary" | "info" | "success" | "warning" | "danger"

default: default

Semantic color.

size

"xs" | "xs-sm" | "sm" | "md" | "lg" | "xl"

default: md

Badge size.

shape

"circle" | "rectangle"

default: rectangle

Badge shape.

isOneChar

boolean

default: false

Forces a perfectly circular badge sized for one character.

Layout & positioning

placement

"top-start" | "top-end" | "bottom-start" | "bottom-end" | ...

default: "top-end"

Corner the badge anchors to, relative to children.

offset

number

default: 0

Extra offset along the placement's main axis.

crossOffset

number

default: 0

Extra offset along the placement's cross axis.

Behavior & states

showOutline

boolean

default: true

Shows a ring around the badge separating it from its content.

disableOutline

boolean

default: false

Alias to force-disable the outline.

isInvisible

boolean

default: false

Hides the badge without unmounting it.

isDot

boolean

default: false

Renders a plain status dot instead of content.

ariaLabel

string

default: —

Accessible label for the badge indicator.

Motion

disableAnimation

boolean

default: false

Disables the bump animation on content change.

Styling

classNames

Partial<Record<"base" | "badge", string>>

default: —

Per-slot class overrides.

className

string

default: —

Class applied to the root element.

style

CSSProperties

default: —

Inline styles for the root element.

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.

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.