Badge

Pill-shaped status badges and labels with solid and outline styles, color variants, optional dot indicator, and removable support.

Preview

Solid variants

import { Badge } from '@/registry/ui-kit/tailwind-badge/react'

function Example() {
  return (
    <div className="flex flex-wrap gap-2">
      <Badge>Default</Badge>
      <Badge variant="primary">Primary</Badge>
      <Badge variant="success">Success</Badge>
      <Badge variant="danger">Danger</Badge>
      <Badge variant="warning">Warning</Badge>
      <Badge variant="info">Info</Badge>
    </div>
  )
}

Outline variants

import { Badge } from '@/registry/ui-kit/tailwind-badge/react'

function Example() {
  return (
    <div className="flex flex-wrap gap-2">
      <Badge outline>Default</Badge>
      <Badge variant="primary" outline>Primary</Badge>
      <Badge variant="success" outline>Success</Badge>
      <Badge variant="danger" outline>Danger</Badge>
      <Badge variant="warning" outline>Warning</Badge>
      <Badge variant="info" outline>Info</Badge>
    </div>
  )
}

With dot

import { Badge } from '@/registry/ui-kit/tailwind-badge/react'

function Example() {
  return (
    <div className="flex flex-wrap gap-2">
      <Badge variant="success" dot>Active</Badge>
      <Badge variant="danger" dot>Offline</Badge>
    </div>
  )
}

Removable

import { Badge } from '@/registry/ui-kit/tailwind-badge/react'

function Example() {
  return (
    <Badge variant="primary" removable onRemove={() => console.log('removed')}>
      React
    </Badge>
  )
}

Component API

PropDefaultDescription
variant"default"Color variant of the badge.
outlinefalseRender as an outline badge with a border and tinted text.
size"md"Size of the badge.
dotfalseShow a colored dot indicator before the text.
removablefalseShow a remove (X) button after the text.
onRemoveCallback fired when the remove button is clicked.
className""Additional CSS classes.

Source Code

"use client";

import { forwardRef } from "react";

/* ── Variant styles ── */

const solidStyles = {
  default: "bg-zinc-100 text-zinc-700 dark:bg-zinc-500/10 dark:text-zinc-400",
  primary: "bg-indigo-50 text-indigo-700 dark:bg-indigo-500/10 dark:text-indigo-400",
  success: "bg-emerald-50 text-emerald-700 dark:bg-emerald-500/10 dark:text-emerald-400",
  danger: "bg-rose-50 text-rose-700 dark:bg-rose-500/10 dark:text-rose-400",
  warning: "bg-amber-50 text-amber-700 dark:bg-amber-500/10 dark:text-amber-400",
  info: "bg-sky-50 text-sky-700 dark:bg-sky-500/10 dark:text-sky-400",
};

const outlineStyles = {
  default: "border border-zinc-200 text-zinc-700 dark:border-zinc-500/30 dark:text-zinc-400",
  primary: "border border-indigo-200 text-indigo-700 dark:border-indigo-500/30 dark:text-indigo-400",
  success: "border border-emerald-200 text-emerald-700 dark:border-emerald-500/30 dark:text-emerald-400",
  danger: "border border-rose-200 text-rose-700 dark:border-rose-500/30 dark:text-rose-400",
  warning: "border border-amber-200 text-amber-700 dark:border-amber-500/30 dark:text-amber-400",
  info: "border border-sky-200 text-sky-700 dark:border-sky-500/30 dark:text-sky-400",
};

const dotColors = {
  default: "bg-zinc-500",
  primary: "bg-indigo-500",
  success: "bg-emerald-500",
  danger: "bg-rose-500",
  warning: "bg-amber-500",
  info: "bg-sky-500",
};

const sizeStyles = {
  sm: "px-1.5 py-0.5 text-xs",
  md: "px-2 py-0.5 text-xs",
  lg: "px-2.5 py-1 text-sm",
};

const dotSizes = {
  sm: "h-1 w-1",
  md: "h-1.5 w-1.5",
  lg: "h-2 w-2",
};

type BadgeVariant = keyof typeof solidStyles;
type BadgeSize = keyof typeof sizeStyles;

/* ── Badge ── */

interface BadgeProps extends React.HTMLAttributes<HTMLSpanElement> {
  variant?: BadgeVariant;
  outline?: boolean;
  size?: BadgeSize;
  dot?: boolean;
  removable?: boolean;
  onRemove?: () => void;
  className?: string;
  children: React.ReactNode;
}

const Badge = forwardRef<HTMLSpanElement, BadgeProps>(
  ({ variant = "default", outline = false, size = "md", dot = false, removable = false, onRemove, className = "", children, ...props }, ref) => {
    const variantClass = outline ? outlineStyles[variant] : solidStyles[variant];
    const sizeClass = sizeStyles[size];

    return (
      <span
        ref={ref}
        className={`inline-flex items-center gap-1.5 rounded-full font-medium ${variantClass} ${sizeClass} ${className}`}
        {...props}
      >
        {dot && (
          <span className={`shrink-0 rounded-full ${dotColors[variant]} ${dotSizes[size]}`} />
        )}
        {children}
        {removable && (
          <button
            type="button"
            onClick={onRemove}
            className="inline-flex shrink-0 items-center justify-center rounded-full p-0.5 opacity-60 transition-opacity hover:opacity-100"
            aria-label="Remove"
          >
            <svg className="h-3 w-3" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
              <path d="M18 6 6 18" />
              <path d="m6 6 12 12" />
            </svg>
          </button>
        )}
      </span>
    );
  }
);
Badge.displayName = "Badge";

export { Badge };
export type { BadgeVariant, BadgeSize };
export default Badge;