GitHub

Badge

A small visual indicator used to highlight status, category, or count. Supports multiple semantic variants and three sizes with automatic dark mode support.

AccessibleDark Mode6 Variants3 Sizes
01

Theme Preview

All six variants across both themes — rendered with forced styling so the comparison is accurate regardless of the current app theme.

Light
DefaultSuccessWarningErrorInfoOutline
sm →Activemd →Reviewlg →Pending
Dark
DefaultSuccessWarningErrorInfoOutline
sm →Activemd →Reviewlg →Pending
02

Live Demo

Size
DefaultSuccessWarningErrorInfoOutline
03

Code Snippet

src/ui/Badge.tsx
import { Badge } from "@/ui/Badge";

// Variants
<Badge.Root variant="default">Default</Badge.Root>
<Badge.Root variant="success">Active</Badge.Root>
<Badge.Root variant="warning">Pending</Badge.Root>
<Badge.Root variant="error">Inactive</Badge.Root>
<Badge.Root variant="info">Review</Badge.Root>
<Badge.Root variant="outline">Draft</Badge.Root>

// Sizes
<Badge.Root size="sm" variant="success">Small</Badge.Root>
<Badge.Root size="md" variant="info">Medium</Badge.Root>
<Badge.Root size="lg" variant="default">Large</Badge.Root>

Backward compatible: API lama <Badge /> tetap didukung, canonical style pakai <Badge.Root />.

04

Copy-Paste (Single File)

Badge.tsx
import type { ReactNode } from "react";

type BadgeVariant = "default" | "success" | "warning" | "error" | "info" | "outline";
type BadgeSize = "sm" | "md" | "lg";

interface BadgeProps {
  variant?: BadgeVariant;
  size?: BadgeSize;
  children: ReactNode;
  className?: string;
}

const cn = (...classes: Array<string | undefined | false>) => classes.filter(Boolean).join(" ");

const VARIANT_CLASSES: Record<BadgeVariant, string> = {
  default: "bg-slate-100 text-slate-700 dark:bg-slate-800 dark:text-slate-300",
  success: "bg-green-100 text-green-700 dark:bg-green-900/40 dark:text-green-400",
  warning: "bg-amber-100 text-amber-700 dark:bg-amber-900/40 dark:text-amber-400",
  error: "bg-red-100 text-red-700 dark:bg-red-900/40 dark:text-red-400",
  info: "bg-blue-100 text-blue-700 dark:bg-blue-900/40 dark:text-blue-400",
  outline: "border border-slate-300 text-slate-600 dark:border-slate-600 dark:text-slate-400",
};

const SIZE_CLASSES: Record<BadgeSize, string> = {
  sm: "text-[10px] px-2 py-0.5",
  md: "text-xs px-2.5 py-0.5",
  lg: "text-sm px-3 py-1",
};

function BadgeRoot({ variant = "default", size = "md", children, className }: BadgeProps) {
  return (
    <span className={cn("inline-flex items-center rounded-full font-medium", VARIANT_CLASSES[variant], SIZE_CLASSES[size], className)}>
      {children}
    </span>
  );
}

type BadgeCompound = typeof BadgeRoot & { Root: typeof BadgeRoot };
export const Badge = Object.assign(BadgeRoot, { Root: BadgeRoot }) as BadgeCompound;
05

Props

PropTypeDefaultDescription
variant"default" | "success" | "warning" | "error" | "info" | "outline""default"Controls the color scheme of the badge.
size"sm" | "md" | "lg""md"Controls padding and font size.
childrenReactNodeThe label content displayed inside the badge.
classNamestringAdditional CSS classes merged via cn().
react-principles