Skip to content
Component installs need the registry setup— the Base UI shadcn project, the @vegastack namespace and the Cloudflare Access service token.
VegaStack Design

Badge

A compact status or label chip — 4 variants × 5 semantic colors × 3 real size tiers, with a dot, a leading icon, and loading.

Status
stable
Since
0.1.0
Accessibility pattern
non-interactive label

Last updated

Active

Install

Add Badge from the VegaStack registry. The CLI verifies the item's integrity hash before writing it.

pnpm dlx shadcn@latest add @vegastack/badge

The same command installs the registry items it composes: @vegastack/spinner.

Usage

import { Badge } from "@/components/ui/badge";

<Badge intent="success" dot>
  Active
</Badge>;

Examples

Variants

The system's variant vocabulary, the same four words Button uses: solid (full-color background with on-color text), soft (a tinted fill — the default), outline (hairline border, no fill — the neutral/hued tag chip), and minimal (ink only, no container at all).

SolidSoftOutlineMinimal

Colors

Color is a semantic token (default, success, warning, destructive, info) — never an arbitrary hex. Each color maps to design-system tokens across every variant: solid (full fill), soft (tint), outline (hairline), and minimal (ink only).

DefaultInfoSuccessWarningDestructive
DefaultInfoSuccessWarningDestructive
DefaultInfoSuccessWarningDestructive

Sizes

Three real height tiers: sm 16px, md 20px, lg 24px. The dot and any composed icon scale with the badge. sm is the dense-table chip — before this it was md with 2px less horizontal padding, which is not a size.

SmallDefaultLarge

States

Add a leading dot indicator, pass an icon to take the dot's place, or set loading to show a spinner and aria-busy. Precedence runs loadingicondot.

With dotWith iconLoadingVerifiedMinimal + icon

On the solid variant the dot switches to bg-current, inheriting the on-color foreground so it stays legible against the filled background.

ActivePendingFailedBeta

Motion

Pass animateIn to pop the badge in (motion-pop-in) when its own appearance is the signal — a status that just flipped, a label freshly applied. It's off by default: a badge rendered as part of a static list (a table column, a filter chip row) must not pop every time its parent re-renders or mounts.

Status:Pending

Minimal — the dense-table treatment

minimal has no background, no border and no horizontal padding, so it sits flush in a table cell rather than faking a pill. Because it has no container, the dot is on by default — the shape is the second channel that keeps status off colour alone (WCAG 1.4.1). Pass dot={false} to drop it, or an icon to take its place.

ActivePendingFailedArchivedPaid

Tag chips — bordered and outline

The Attio-teardown chip formula: bordered draws the matching-hue hairline on the soft tint (crisper edges on white); variant="outline" is the fill-less neutral/hued tag. Compose a +N overflow as a neutral outline badge — or reach for TagGroup, which does it for you.

SyncingActiveDegradedFailed
B2BSaaSEnterprise+3

Playground

Mix variant, color, size, and the dot / loading / animate-in states, then copy the generated JSX.

Active
<Badge>Active</Badge>

API Reference

PropTypeDefaultDescription
animateInbooleanfalseOpt-in mount animation (motion-pop-in, a scale + fade "arrival") for a badge that appears in response to a real event — e.g. a status that just flipped to "Verified", a freshly-applied label, or a badge toggled on by a user action. **Default off**: a badge rendered as part of a static list (a table column, a filter chip row) must not pop every time its parent re-renders or mounts. Set it only where the badge's own appearance IS the signal.
borderedbooleanfalseDraw the matching-hue hairline border on the soft tint (the crisp "chip" read on white surfaces). No-op on other variants.
dotbooleanShow a small leading dot indicator colored by intent. Ignored while loading, and replaced by icon when one is given. Defaults to true on variant="minimal" and false everywhere else: a minimal badge has no container, so the dot is the only non-colour carrier of its status (WCAG 1.4.1). Pass dot={false} to opt a minimal badge out.
iconReact.ReactNodeLeading icon, rendered in the dot's place. Pass a lucide-react element (or Icon) — it is sized by the badge's own [&_svg] rule, so do not set size on it. Ignored while loading.
intent"default" | "destructive" | "info" | "success" | "warning"'default'Semantic intent family. Maps to design-system tokens only — never an arbitrary hex or color-mix value. default is the neutral muted badge.
loadingbooleanfalseReplace the leading content with a spinner and set aria-busy. Takes precedence over icon and dot.
renderuseRender.RenderProp<Record<string, unknown>>Replace the rendered element via Base UI render composition. Pass a ReactElement or a render function.
size"lg" | "md" | "sm"'md'Size tier — three real heights: sm 16px (dense tables), md 20px, lg 24px. The dot and any composed icon scale with the badge.
variant"minimal" | "outline" | "soft" | "solid"'soft'Visual treatment — the system's variant vocabulary, shared with Button. - solid: family fill with on-color text. - soft: {family}-subtle tint + {family}-text (default). - outline: hairline chip, no fill — the neutral/hued tag treatment. - minimal: ink only — no fill, no border, no horizontal padding, and a leading dot by default so status is never signalled by colour alone.

Accessibility

  • Renders a non-interactive <span> by default — a badge is a label, not a control.
  • The dot and loading spinner are decorative (aria-hidden); the badge text carries the meaning, so never rely on color or the dot alone to convey status.
  • loading sets aria-busy="true" while content is updating.
  • The loading spinner uses animate-spin; it freezes under prefers-reduced-motion through the global reset, which every component inherits rather than restating.
  • variant="minimal" turns the dot on by default: without a container, the dot is what keeps the status from resting on colour alone.
  • Use render to compose with a link when a badge needs to navigate, keeping the surrounding text as the accessible name.
ContractStates tested
Behaviourdefault, error, loading, success
Accessibilitybusy
Visualdefault, loading, error, success

Do / Don't

Do
Use a semantic color to signal status — success for active, destructive for errors — with text that states the meaning.
Don't
Rely on the dot or color alone to convey state, or use a Badge as a clickable button.

On this page