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.
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
| Prop | Type | Default | Description |
|---|---|---|---|
| children | ReactNode | — | Element the badge is anchored to. |
| content | string | number | ReactNode | — | Badge content; omit with isDot for a plain dot. |
| max | number | — | Caps a numeric content at max+ (e.g. "99+"). |
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
| Prop | Type | Default | Description |
|---|---|---|---|
| variant | "solid" | "soft" | "bordered" | solid | Visual style variant. |
| shadow | "none" | "xs" | "xs-sm" | "sm" | "md" | "lg" | none | Box-shadow depth. |
| color | "default" | "primary" | "secondary" | "info" | "success" | "warning" | "danger" | default | Semantic color. |
| size | "xs" | "xs-sm" | "sm" | "md" | "lg" | "xl" | md | Badge size. |
| shape | "circle" | "rectangle" | rectangle | Badge shape. |
| isOneChar | boolean | false | Forces a perfectly circular badge sized for one character. |
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
| Prop | Type | Default | Description |
|---|---|---|---|
| placement | "top-start" | "top-end" | "bottom-start" | "bottom-end" | ... | "top-end" | Corner the badge anchors to, relative to children. |
| offset | number | 0 | Extra offset along the placement's main axis. |
| crossOffset | number | 0 | Extra offset along the placement's cross axis. |
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
| Prop | Type | Default | Description |
|---|---|---|---|
| showOutline | boolean | true | Shows a ring around the badge separating it from its content. |
| disableOutline | boolean | false | Alias to force-disable the outline. |
| isInvisible | boolean | false | Hides the badge without unmounting it. |
| isDot | boolean | false | Renders a plain status dot instead of content. |
| ariaLabel | string | — | Accessible label for the badge indicator. |
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
| Prop | Type | Default | Description |
|---|---|---|---|
| disableAnimation | boolean | false | Disables the bump animation on content change. |
disableAnimation
boolean
default: false
Disables the bump animation on content change.
Styling
| Prop | Type | Default | Description |
|---|---|---|---|
| classNames | Partial<Record<"base" | "badge", string>> | — | Per-slot class overrides. |
| className | string | — | Class applied to the root element. |
| style | CSSProperties | — | Inline styles for the root element. |
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.