Badge

A notification badge component for displaying counts, icons, or status indicators. Wraps children and positions the badge indicator relative to them.

Usage

Basic Count Badge

Display a count badge on any element. The badge center sits exactly on the corner of the child element.

A512
99
<Badge count={5} aria-label="5 notifications">
  <Avatar user={user} />
</Badge>

<Badge count={12} aria-label="12 messages">
  <Button label="Messages" />
</Badge>

Empty Badge (Dot Indicator)

Omit count and icon to render a small dot indicator. Useful for presence or unread status.

A
{/* Empty badge - just a dot indicator */}
<Badge status="danger" aria-label="Unread">
  <Icon icon="mail" />
</Badge>

<Badge status="success" aria-label="Online">
  <Avatar user={user} />
</Badge>

Icon Badge

Display an icon instead of a count.

<Badge icon="bell" status="danger" aria-label="New alerts">
  <Button>Notifications</Button>
</Badge>

Positioning

Position the badge on any corner. The badge center aligns with the corner point.

A1

top-right (default)

A2

top-left

A3

bottom-right

A4

bottom-left

A5

center

<Badge count={1} position="top-right" aria-label="...">
  <Avatar user={user} />
</Badge>

<Badge count={2} position="top-left" aria-label="...">
  <Avatar user={user} />
</Badge>

<Badge count={3} position="bottom-right" aria-label="...">
  <Avatar user={user} />
</Badge>

<Badge count={4} position="bottom-left" aria-label="...">
  <Avatar user={user} />
</Badge>

{/* Center - useful when badge is the main focus */}
<Badge count={5} position="center" aria-label="...">
  <Avatar user={user} />
</Badge>

Shapes

Use the shape prop to control the badge shape. Without a shape prop, the badge renders as a pill that expands to fit content.

A3

pill (default)

A3

circle (0)

A3

triangle (3)

A3

square (4)

A3

pentagon (5)

{/* Pill shape (default) - expands to fit content */}
<Badge count={123} aria-label="123 items">...</Badge>

{/* Circle */}
<Badge count={3} shape={0} aria-label="3 items">...</Badge>

{/* Triangle */}
<Badge count={3} shape={3} aria-label="3 items">...</Badge>

{/* Square */}
<Badge count={3} shape={4} aria-label="3 items">...</Badge>

{/* Pentagon */}
<Badge count={3} shape={5} aria-label="3 items">...</Badge>

Empty Shapes (Dot Indicators)

Shapes can also be used without content to create shaped dot indicators.

A

pill (default)

A

circle

A

triangle

A

square

A

pentagon

{/* Empty shaped badges - no count or icon */}
<Badge status="danger" shape={0} aria-label="Alert">...</Badge>
<Badge status="danger" shape={3} aria-label="Alert">...</Badge>
<Badge status="danger" shape={4} aria-label="Alert">...</Badge>

Hollow Empty Shapes

Empty shapes with the hollow variant for subtle indicators.

A

pill hollow

A

circle hollow

A

square hollow

Count Overflow

When a shape is specified, counts over 9 display as "9+" in a circle. Pill shapes expand to show the full number.

A123

pill: shows 123

A9+

circle: shows 9+

A9+

square: shows 9+

Variants

Use the variant prop to switch between solid and hollow styles. Both variants can display counts, icons, or be empty.

A5

solid with count

A

solid empty

A5

hollow with count

A

hollow empty

{/* Solid - filled background */}
<Badge count={5} variant="solid" status="info" aria-label="5">
  <Avatar user={user} />
</Badge>

{/* Hollow - outlined style */}
<Badge count={5} variant="hollow" status="info" aria-label="5">
  <Avatar user={user} />
</Badge>

Status Colors

Badge integrates with the Status system for semantic coloring.

A1

neutral

A2

info

A3

success

A4

warning

A5

danger

Hollow Variant with Status

AAAAA

In Containers

Badge automatically matches its outer border color to the container background using ContainerContext. This provides visual separation between the badge and its surroundings.

A3
A5
A7
<Container>
  <Badge count={3} status="danger" aria-label="3 items">
    <Avatar user={user} />
  </Badge>
</Container>

<Container status="info">
  <Badge count={5} status="danger" aria-label="5 items">
    <Avatar user={user} />
  </Badge>
</Container>

Visibility Control

Use the show prop to control badge visibility. Note that count=0 displays "0" and does not auto-hide.

A5

show=true

A5

show=false

A0

count=0 (visible)

{/* Hidden badge */}
<Badge count={5} show={false} aria-label="5 notifications">
  <Avatar user={user} />
</Badge>

{/* count=0 displays "0" */}
<Badge count={0} aria-label="0 items">
  <Avatar user={user} />
</Badge>

Clickable Badge

Add an onClick handler to make the badge itself interactive, separate from the wrapped children. Supports keyboard navigation (Enter/Space).

3
<Badge count={3} aria-label="3 messages" onClick={() => openMessages()}>
  <Icon icon="chat" />
</Badge>

Props

NameTypeDefaultDescription
children*JSXElement-

Element to wrap with badge

aria-label*string-

Accessibility label for screen readers

countnumberundefined

Number to display (takes precedence over icon). Capped at 9+ for polygon shapes.

iconstringundefined

Icon to display (ignored if count provided)

position"top-right" | "top-left" | "bottom-right" | "bottom-left" | "center""top-right"

Position of badge. Corner positions place badge on edges; center places badge in the middle of the child.

shape0 | 3 | 4 | 5undefined

Number of vertices (0=circle, 3=triangle, 4=square, 5=pentagon). If omitted, uses pill shape.

variant"solid" | "hollow""solid"

Visual variant

statusStatus"neutral"

Status color theme (success, danger, warning, info, neutral)

showbooleantrue

Controls badge visibility

onClick() => voidundefined

Click handler for the badge itself

rootStylesStyleXStylesundefined

Custom StyleX styles for the wrapper element

badgeStylesStyleXStylesundefined

Custom StyleX styles for the badge indicator itself. Useful for custom positioning.

inlinebooleanfalse

Use inline display for text flow (e.g., in SmartStrings). Default is inline-flex for proper box model.

* Required prop

Theme Variables

Badge appearance can be customized via theme variables in themeDetails:

// In theme.stylex.ts
export const themeDetails = stylex.defineVars({
  // Badge sizing
  badgeSize: themeSizing.spaceM,
  badgeDotSize: `calc(${themeSizing.spaceXS} * 2)`,
  badgeFontSize: themeSizing.font0,

  // Badge padding
  badgePaddingInline: `calc(${themeSizing.spaceXS} * 0.75)`,
  badgePaddingBlock: `calc(${themeSizing.spaceXS} * 0.25)`,

  // Badge borders
  badgeBorderWidth: `calc(${themeSizing.spaceXS} * 0.5)`,
  badgeInnerBorderWidth: `calc(${themeSizing.spaceXS} * 0.4)`,

  // Badge icon
  badgeIconSize: `calc(${themeSizing.spaceM} * 0.65)`,
});

Accessibility

aria-label - Required prop for screen reader accessibility

role="status" - Badge uses status role to indicate it contains live content

Clickable badges support keyboard navigation with Enter and Space keys

Interactive badges receive focus with tabIndex when onClick is provided