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 / archived / paused / failed / warning), and BadgeNumber (pill count — 8 / 12 / 99+). For anything clickable use Button.

Updated Aug 21, 2026 by Leonardo Posada

Interactive playground

Toggle the props in the panel on the right and see the Badge re-render in real time. The code block below reflects the exact JSX with the values you picked, ready to copy into your prototype.

Approved
Playground
<Badge variant="default">"Approved"</Badge>

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|archived|paused|failed|warning'; 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').

CreatedPendingSucceededDeclinedRefundedInactiveArchivedPausedFailedWarning

Custom mode (any domain)

When the prototype is not payments (Reports, Notifications, Audit logs, Users, etc.), skip the preset and pass tone + icon + label instead. The tone maps to the same semantic palette the presets use (success / warning / destructive / info / neutral), so the badge still reads as a Yuno badge, just carrying a label the payments vocabulary does not cover. Preset and custom mode are mutually exclusive at the type level.

DraftPublishedScheduledUnread
{/* Custom mode: tone + icon + label instead of a preset status. */}
<StatusBadge tone="neutral" icon={FileDashed} label="Draft" />
<StatusBadge tone="success" icon={CloudArrowUp} label="Published" />
<StatusBadge tone="info" icon={CalendarBlank} label="Scheduled" />
<StatusBadge tone="warning" icon={EnvelopeSimpleOpen} label="Unread" />

Long labels

A badge is read as one object, so it never wraps and never runs past maxChars (default 20). Past the cap the text is clipped with a real ellipsis and the full string moves into a Tooltip — which exists ONLY when something was actually cut, because a tooltip that repeats what is already on screen teaches people to ignore tooltips. Icons composed as children are never counted or clipped. The cap is about LENGTH, not fit: a badge carrying a sentence is wrong at any width, and should read the same wherever it appears.

Not publishedNetwork tokens offRequires manual revi…Insufficient funds o…
{/* Under the cap: rendered as-is, no wrapper, no tooltip. */}
<Badge variant="secondary">Not published</Badge>
<Badge variant="secondary">Network tokens off</Badge>

{/* Past it: clipped with a real ellipsis, full text in a Tooltip. */}
<Badge variant="secondary">Requires manual review before capture</Badge>
<StatusBadge status="declined">Insufficient funds on the issuing account</StatusBadge>

{/* Raise the cap where a surface genuinely needs a longer label. */}
<Badge variant="secondary" maxChars={40}>Requires manual review before capture</Badge>

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" | "archived" | "paused" | "failed" | "warning"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.