# Badge

> A React badge for status chips, removable filters and animated counts in three variants and six tones, plus a count or dot on any corner.

- Type: React component (Data Display)
- Access: Free (the source is open to everyone)
- Version: 1.0.0
- Install: `npx wingo-ui@latest add badge`
- Docs: https://wingo-ui.com/components/badge

## Interaction

A status badge is plain text in a tinted pill; with pulse its dot pings like a live light. A count rolls to the new number, up when it grows and down when it shrinks, the pill widens on a smooth spring and gives a small bounce on every new item; at 0 it scales away. Removable chips show a round x: click or tap it (a 36px target on touch), or focus it with Tab and press Enter, Backspace or Delete. With asChild the badge is a real link or button with a hover tint, a press dip, a focus ring and a 44px tall touch target. BadgeAnchor pins a badge to a corner of an icon button or avatar, cut out by a thin ring, and pops it in and out.

## Installation

```bash
npx wingo-ui@latest add badge
```

The CLI adds the files, their dependencies and a version record for updates.

With shadcn: `npx shadcn@latest add https://wingo-ui.com/r/badge.json`

Files:

- `components/ui/badge.tsx`

npm dependencies: `npm i @radix-ui/react-slot lucide-react motion`

Wingo UI dependencies (installed automatically):

- [Motion presets](https://wingo-ui.com/components/motion) ([Markdown](https://wingo-ui.com/components/motion.md))
- [Tones](https://wingo-ui.com/components/tones) ([Markdown](https://wingo-ui.com/components/tones.md))
- [UIProvider](https://wingo-ui.com/components/ui-config) ([Markdown](https://wingo-ui.com/components/ui-config.md))

## Usage

```tsx
import { Bell } from "lucide-react"
import { Badge, BadgeAnchor } from "@/components/ui/badge"
import { Button } from "@/components/ui/button"

export function OrderHeader({ unread, onOpenInbox }: { unread: number; onOpenInbox: () => void }) {
  return (
    <div className="flex items-center gap-3">
      <Badge tone="success" dot>Paid</Badge>
      <Badge tone="warning">Low stock</Badge>
      <BadgeAnchor offset={5} badge={<Badge variant="solid" tone="danger" size="sm" count={unread} aria-hidden />}>
        <Button variant="soft" size="icon" radius="full" aria-label={`Notifications, ${unread} unread`} onClick={onOpenInbox}>
          <Bell />
        </Button>
      </BadgeAnchor>
    </div>
  )
}
```

## Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `children?` | `ReactNode` | `"Nou"` | The label. Long labels truncate with an ellipsis. Leave it empty (with no icons or count) and the badge becomes a status dot. |
| `variant?` | `"solid" \| "soft" \| "outline"` | `"soft"` | soft (a tinted pill) for statuses, solid for counts and the one label that must stand out, outline for filters and quiet tags. |
| `tone?` | `"neutral" \| "brand" \| "success" \| "warning" \| "danger" \| "info"` | `"neutral"` | Semantic color from the design tokens: success for paid or online, warning for low stock, danger for cancelled or failed, info for in progress. Pair color with the text, never color alone. |
| `color?` | `string` | `""` | Any CSS color. Replaces the tone in every variant; text on a solid fill turns black or white by lightness. Leave empty to use the tone. |
| `size?` | `"sm" \| "md" \| "lg"` | `"md"` | Height 20 / 24 / 28px with 11 / 12 / 13px text and 12 / 12 / 14px icons. |
| `radius?` | `"none" \| "sm" \| "md" \| "lg" \| "full"` | `"full"` | Corner shape: 0 / 4 / 6 / 8px or a pill. Badges default to pills (a deliberate difference from controls); the square steps suit tag-like labels. |
| `dot?` | `boolean` | `false` | A leading status dot in the tone (in the text color on a solid badge). |
| `pulse?` | `boolean` | `false` | The dot pings, for live, recording or online. Implies dot. Decorative (hidden from screen readers); under reduced motion the dot stays still. |
| `max?` | `number` | `99` | Counts above it read "99+". Values below 1 fall back to 99. Only used with count. |
| `showZero?` | `boolean` | `false` | A count of 0 scales the badge away unless this is set. Try it on the bell: tap it to mark all as read. |
| `locale?` | `string` | `""` | Formats the count through Intl, e.g. "en-US" turns 1250 into 1,250 and "de-DE" into 1.250. Empty uses the reader's locale; pass one for server-rendered counts of 1000 or more so the grouping matches after hydration. |
| `removeLabel?` | `string` | `"Remove"` | aria-label of the remove button, e.g. "Remove the Brooklyn filter". Only used with onRemove. |
| `disabled?` | `boolean` | `false` | Dims the badge (a count too) and hides the remove button. With asChild the link gets aria-disabled, leaves the tab order and ignores clicks. |
| `asChild?` | `boolean` | `false` | Renders your single child (an <a>, a router Link, a button) as the badge, with a hover tint, a press dip and a focus ring. asChild badges have no remove button, since a button inside a link is invalid. The demo swaps in a link. |
| `count?` | `number` |  | Shows this number instead of children and animates every change: the digits slide, the width follows on a spring and the badge bumps when it grows. Negative values clamp to 0, fractions round. The demo's bell drives it with the + and - buttons. |
| `leftIcon?` | `ReactNode` |  | An icon before the label. Unsized svgs follow the size (12 / 12 / 14px). |
| `rightIcon?` | `ReactNode` |  | An icon after the label, like an arrow on a link badge. |
| `onRemove?` | `() => void` |  | Shows a round x button that calls it on click, tap, Enter, Backspace or Delete. The click does not bubble, so a chip inside a clickable row removes itself without opening the row. |
| `classNames?` | `Partial<Record<"dot" \| "icon" \| "label" \| "count" \| "remove", string>>` |  | Extra classes for the inner parts: the status dot, each icon wrapper, the label, the count and the remove button. |
| `className?` | `string` |  | Extra classes for the badge itself, merged last. Override the tone vars here, e.g. [--tone-foreground:white]. |

## Parts

### BadgeAnchor (component)

Pins a badge to a corner of its child (an icon button, an avatar). badge: the Badge to place (a count, or an empty Badge for a dot). placement: "top-end" (default) | "top-start" | "bottom-end" | "bottom-start", logical so it flips in RTL. offset: px, default 0; positive moves the badge inward, about 5 to 6px puts it on the edge of a round 40px host. invisible: scales it out and back in. ringColor: default var(--background), the 2px ring that cuts the badge out of its host (pass var(--surface) on panels). classNames.badge: the placed wrapper, which lets clicks through to the host. Props: BadgeAnchorProps.

## Examples

- **Variants and tones**: Three variants across the six tones, each with a status it would really carry.
- **Sizes and radii**: 20, 24 and 28px. Pills by default; the square steps suit tag-like labels.
- **Dot and pulse**: A status dot, and a ping for anything live. An empty badge is a dot on its own.
- **Counts**: Digits roll with the change and 128 reads 99+. The last row is de-DE with max 9999. Click a row to clear it.
- **Removable filter chips**: Click the x or focus it and press Delete; the rest slide into place and focus moves to the next chip.
- **Anchored to an icon or avatar**: A count on a bell, dots for unread and online, cut out by a ring in the panel's color.
- **Custom color and links**: Any CSS color, and asChild for a badge that is a real link with hover, press and focus.
- **Long labels**: A badge never grows past its container.

## See also

- [Avatar](https://wingo-ui.com/components/avatar) ([Markdown](https://wingo-ui.com/components/avatar.md))
- [Button](https://wingo-ui.com/components/button) ([Markdown](https://wingo-ui.com/components/button.md))
- [Animated Counter](https://wingo-ui.com/components/animated-counter) ([Markdown](https://wingo-ui.com/components/animated-counter.md))
- [Notification Bell](https://wingo-ui.com/components/notification-bell) ([Markdown](https://wingo-ui.com/components/notification-bell.md))
- [Tones](https://wingo-ui.com/components/tones) ([Markdown](https://wingo-ui.com/components/tones.md))

---

Canonical: https://wingo-ui.com/components/badge
More components: https://wingo-ui.com/components · Index for agents: https://wingo-ui.com/llms.txt
