Components

Badge

Small non-interactive tag. Three variants ship in the kit: Badge (solid label — default / secondary / outline / destructive / verified), StatusBadge (soft-tinted transaction/entity status with a filled icon: created / pending / succeeded / declined / refunded / inactive), and BadgeNumber (pill count — 8 / 12 / 99+). For anything clickable use Button.

Updated Jul 18, 2026 by Leonardo Posada

Anatomy

DefaultSucceeded8
  1. 1
    Badge (solid label)

    inline-flex rounded-md px-2 py-0.5 text-xs font-semibold. Icons composed as children auto-size to 12px via [&>svg]:size-3. Variants: default (primary) / secondary / outline / destructive / verified (blue-500).

  2. 2
    StatusBadge (status pill)

    Soft-tinted (color/15 bg + color/foreground text) with a filled Phosphor icon and a canonical label per status. Pass status='created|pending|succeeded|declined|refunded|inactive'; override children to change the label.

  3. 3
    BadgeNumber (count pill)

    h-5 min-w-5 rounded-full for counts. Same variants as Badge. Use for notification bells, inbox counters, unread markers.

Variants

DefaultSecondaryOutlineDestructiveVerified
<Badge>Default</Badge>
<Badge variant="secondary">Secondary</Badge>
<Badge variant="outline">Outline</Badge>
<Badge variant="destructive">Destructive</Badge>
<Badge variant="verified">
  <SealCheck weight="fill" />
  Verified
</Badge>

With icons

ApprovedNext
<Badge>
  <CheckCircle weight="light" />
  Approved
</Badge>

<Badge variant="secondary">
  Next
  <ArrowRight weight="light" />
</Badge>

Status map (StatusBadge)

Each status ships with a token color + filled Phosphor icon + default label. Override the label via children when the domain wording differs ('Approved' vs 'Succeeded').

CreatedPendingSucceededDeclinedRefundedInactive

Number

812399+
<BadgeNumber>8</BadgeNumber>
<BadgeNumber variant="secondary">12</BadgeNumber>
<BadgeNumber variant="outline">3</BadgeNumber>
<BadgeNumber variant="destructive">99+</BadgeNumber>
Three atoms, one file
Badge, StatusBadge and BadgeNumber live in the same file (src/components/ui/badge.tsx) because they share the same visual language and are almost always chosen against each other. Pick Badge for labels/tags, StatusBadge for canonical states with icons, BadgeNumber for counts.

Recipes

Ready-to-copy compositions covering the most common Yuno usages of this atom.

Transaction status in a table row

The canonical usage: StatusBadge inside a table cell to communicate a payment/transaction state at a glance. Use the domain wording via children when it differs from the default label.

TransactionAmountStatus
tx_a1b2$ 145.20Approved
tx_c3d4$ 89.00Pending
tx_e5f6$ 320.50Declined
<Table>
  <TableHeader>
    <TableRow>
      <TableHead>Transaction</TableHead>
      <TableHead>Amount</TableHead>
      <TableHead>Status</TableHead>
    </TableRow>
  </TableHeader>
  <TableBody>
    <TableRow>
      <TableCell>tx_a1b2</TableCell>
      <TableCell>$ 145.20</TableCell>
      <TableCell><StatusBadge status="succeeded">Approved</StatusBadge></TableCell>
    </TableRow>
    <TableRow>
      <TableCell>tx_c3d4</TableCell>
      <TableCell>$ 89.00</TableCell>
      <TableCell><StatusBadge status="pending" /></TableCell>
    </TableRow>
    <TableRow>
      <TableCell>tx_e5f6</TableCell>
      <TableCell>$ 320.50</TableCell>
      <TableCell><StatusBadge status="declined" /></TableCell>
    </TableRow>
  </TableBody>
</Table>
Notification count on an IconButton

BadgeNumber pinned top-right of a bell/inbox IconButton via relative + absolute positioning. Cap at 99+ so it does not push the icon.

8
<div className="relative inline-block">
  <Button variant="ghost" size="icon" aria-label="Notifications">
    <BellSimple weight="light" />
  </Button>
  <BadgeNumber
    variant="destructive"
    className="absolute -right-1 -top-1 pointer-events-none"
  >
    8
  </BadgeNumber>
</div>
Category tag next to a title

Solid Badge (outline / secondary) placed after a title to categorize a resource (Rule type, Plan tier, Environment). Keep to one tag per title so it does not become noise.

High-value BIN block

Fraud rule
<div className="flex items-center gap-2">
  <h3 className="text-lg font-semibold text-foreground">High-value BIN block</h3>
  <Badge variant="outline">Fraud rule</Badge>
</div>
Verified / trusted flag

verified variant with a leading SealCheck (fill) to mark an account, merchant or method as verified. Reserved for trust signals — never for status.

Acme Merchants Ltd.Verified
<div className="flex items-center gap-2">
  <span className="text-sm font-medium text-foreground">Acme Merchants Ltd.</span>
  <Badge variant="verified">
    <SealCheck weight="fill" />
    Verified
  </Badge>
</div>

Import

Copy this import line at the top of the file where you compose this atom.

Three atoms, one file. Pick Badge for labels, StatusBadge for canonical states with icons, BadgeNumber for counts.
import { Badge, BadgeNumber, StatusBadge } from "@/components/ui/badge";

Props

Everything else from the underlying HTML or Radix primitive is forwarded via ...props.

PropTypeDefaultDescription
Badge.variant"default" | "secondary" | "outline" | "destructive" | "verified""default"Solid label variants. Default = primary; verified = blue-500 (trust flag).
Badge.classNamestringMerged. Icons composed as children auto-size to 12px via [&>svg]:size-3.
StatusBadge.status"created" | "pending" | "succeeded" | "declined" | "refunded" | "inactive"Required. Picks the color token + filled Phosphor icon + default label.
StatusBadge.childrenReact.ReactNodeOptional label override. Use when domain wording differs from the default (Approved vs Succeeded).
BadgeNumber.variant"default" | "secondary" | "outline" | "destructive""default"Same palette as Badge, without verified (counts do not signal trust).
BadgeNumber.childrenReact.ReactNodeThe count. Cap display at 99+ so the pill does not push its neighbor.
badgeVariants / badgeNumberVariants / statusConfighelpersExported for advanced composition. badgeVariants and badgeNumberVariants return the class string via CVA; statusConfig is the color+icon+label map keyed by status.

When to use

  • Transaction / entity status inside table rows (StatusBadge).
  • Category or tag next to a title (Badge).
  • Count on an IconButton (BadgeNumber — inbox, notifications, unread).
  • Verified / trusted flag on an identity (Badge variant='verified').

When not to use

  • Anything clickable — use Button or a chip with a close/action.
  • Body text emphasis — use bold or a Callout.
  • Blocking messages — use Dialog or AlertDialog.
  • Persistent notices above a form — use Alert.

Usage

Do
  • Use the variant that matches meaning: success = approved, destructive = declined, verified = trusted identity.
  • Keep the label to 1-2 words.
  • For counts, cap at 99+ so the pill does not push its neighbor.
  • For StatusBadge, override children only when the domain wording differs (Approved vs Succeeded).
Don't
  • Don't stack more than 2 Badges next to a single title — it becomes noise.
  • Don't make Badges clickable — use a Button variant instead.
  • Don't invent a new status color — reach for one of the six StatusBadge tones or map yours to the closest one.
  • Don't use variant='destructive' as decoration — reserve red for real declines or errors.

Related

Cross-links to atoms and patterns you may reach for next.

  • ButtonFor clickable chips or pills — Badge is non-interactive by design.
  • AlertFor a full-width banner instead of an inline tag.
  • CalloutFor a tinted docs box that emphasizes body text.
  • TooltipWrap a Badge to explain what the status means on hover.