Badge

A Badge component with the possibility of adding an icon and a status

Dependencies

Source Code

"use client";
 
import { FC, HTMLAttributes, PropsWithChildren } from "react";
import { VariantProps } from "cva";
import { Slot } from "@/components/slot";
 
import { cva, cn } from "@/lib/utils";
 
const badgeVariants = cva({
  base: "inline-flex items-center gap-1 rounded-full font-semibold leading-none ring-1 ring-inset [&>[data-badge-icon]:first-child]:-ml-0.5 [&>[data-badge-icon]:last-child]:-mr-0.5",
  variants: {
    variant: {
      neutral: "bg-background text-foreground/80 ring-foreground/10",
      success: "bg-emerald-500/10 text-emerald-600 ring-emerald-600/20",
      error: "bg-red-500/10 text-red-600 ring-orange-600/20",
      warning: "bg-yellow-500/10 text-yellow-600 ring-yellow-600/20",
      info: "bg-blue-500/10 text-blue-500 ring-blue-500/20",
    },
    size: {
      md: "h-6 px-2.5 text-sm",
      sm: "h-5 px-2 text-xs",
      xs: "h-4 px-1.5 text-[10px]",
    },
  },
  defaultVariants: {
    size: "md",
  },
});
 
interface BadgeProps extends React.ComponentPropsWithRef<"div"> {
  variant?: VariantProps<typeof badgeVariants>["variant"];
  size?: VariantProps<typeof badgeVariants>["size"];
  asChild?: boolean;
}
 
const Badge = ({
  ref,
  children,
  variant = "neutral",
  size = "md",
  className,
  asChild,
  ...rest
}: BadgeProps) => {
  const Comp = asChild ? Slot : "div";
 
  return (
    <Comp
      ref={ref}
      className={cn(badgeVariants({ variant, size }), className)}
      {...rest}
    >
      {children}
    </Comp>
  );
};
 
const BadgeIcon: FC<PropsWithChildren<HTMLAttributes<HTMLDivElement>>> = ({
  children,
  className,
  ...rest
}) => {
  return (
    <div
      data-badge-icon
      className={cn("flex items-center justify-center", className)}
      {...rest}
    >
      {children}
    </div>
  );
};
 
type BadgeStatusProps = Omit<HTMLAttributes<HTMLDivElement>, "children">;
 
const BadgeStatus: FC<PropsWithChildren<BadgeStatusProps>> = ({
  className,
  ...rest
}) => {
  return (
    <div
      className={cn(
        "mx-0.5 size-1.5 rounded-full bg-current first:ml-0 last:mr-0",
        className
      )}
      {...rest}
    />
  );
};
 
export { Badge, BadgeIcon, BadgeStatus };

Anatomy

<Badge>
  <BadgeIcon />
  <BadgeStatus />
</Badge>

API Reference

Badge

Extends the div element.

PropDefaultType

variant

"neutral"

"neutral"

"success"

"error"

"warning"

"info"

size

"md"

"xs"

"sm"

"md"

asChild

-

boolean

BadgeIcon

Extends the div element.

BadgeStatus

Extends the div element.

Examples

Variants

Sizes

With icon and status

Best Practices

  1. Usage:

    • Use appropriate variants for context
    • Keep content concise
    • Consider icon visibility at small sizes
  2. Accessibility:

    • Ensure sufficient color contrast
    • Provide clear meaning through text