Skip to content
Corsair UI
Browse the docs

ComponentsNo. 06 of 109

Badge

Short status or metadata label. Solid, secondary and outline styles, plus success, warning and destructive statuses shown as a coloured dot.

Installation

pnpm dlx shadcn@latest add @corsair-ui/badge

No setup needed: the CLI finds @corsair-ui in the shadcn registry directory. The files land in your project as source: edit them like your own code. More in Installation.

Also adds
utils
npm
@radix-ui/react-slotclass-variance-authorityclsxtailwind-merge

Examples

Styles

DefaultSecondaryOutline

Status

Success, warning and destructive keep a neutral outline and show the tone as a dot.

PaidPendingFailed

With icons

An icon replaces the dot and takes the tone.

VerifiedAwaiting reviewBlockedFeatured

API

PropTypeDefault
variant

Status tones draw a coloured dot, or colour the first icon.

"default" | "secondary" | "outline" | "success" | "warning" | "destructive""default"
asChild

Style a link as a badge.

booleanfalse

Accessibility

  • The colour is decoration: the text has to say what the status is.

Compatibility

Tailwind CSS
3.4 and 4, both checked in CI
React
19
Rendering
Works in server components

Source

1 file, installed as source you own
badge.tsxView on GitHub
import { Slot } from "@radix-ui/react-slot";
import { cva, type VariantProps } from "class-variance-authority";
import type { ComponentProps } from "react";

import { cn } from "@/registry/default/lib/utils";

const badgeVariants = cva(
  [
    "inline-flex w-fit shrink-0 items-center justify-center gap-1.5 overflow-hidden rounded-md border px-2 py-0.5 text-xs font-medium whitespace-nowrap",
    "transition-[color,background-color,border-color,box-shadow] outline-none motion-reduce:transition-none",
    "focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
    "[&>svg]:pointer-events-none [&>svg]:size-3",
  ],
  {
    variants: {
      variant: {
        default: "bg-primary text-primary-foreground [a&]:hover:bg-primary/90 border-transparent",
        secondary:
          "bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/80 border-transparent",
        outline:
          "border-border text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground",
        // Status tones keep the neutral outline and put the colour on a dot,
        // or on the icon when there is one.
        destructive:
          "border-border text-foreground before:bg-destructive [&>svg]:text-destructive [a&]:hover:bg-accent before:size-1.5 before:shrink-0 before:rounded-full has-[>svg]:before:hidden",
        success:
          "border-border text-foreground before:bg-success [&>svg]:text-success [a&]:hover:bg-accent before:size-1.5 before:shrink-0 before:rounded-full has-[>svg]:before:hidden",
        warning:
          "border-border text-foreground before:bg-warning [&>svg]:text-warning [a&]:hover:bg-accent before:size-1.5 before:shrink-0 before:rounded-full has-[>svg]:before:hidden",
      },
    },
    defaultVariants: {
      variant: "default",
    },
  }
);

interface BadgeProps extends ComponentProps<"span">, VariantProps<typeof badgeVariants> {
  /** Render the single child element (a link, for example) with the badge styles instead. */
  asChild?: boolean;
}

/**
 * Short status or metadata label. The status tones (success, warning,
 * destructive) show a coloured dot, or colour the icon if you put one first;
 * the text stays neutral, so it has to say what the status is.
 */
function Badge({ className, variant, asChild = false, ...props }: BadgeProps) {
  const Comp = asChild ? Slot : "span";
  return (
    <Comp
      data-slot="badge"
      data-variant={variant ?? "default"}
      className={cn(badgeVariants({ variant }), className)}
      {...props}
    />
  );
}

export { Badge, badgeVariants, type BadgeProps };