Octocat

Badge

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

Up to date
import { PackageIcon } from '@phosphor-icons/react/dist/ssr';

import { Badge, BadgeIcon, BadgeStatus } from '@/components/badge';

export default function BadgePreview() {
  return (
    <Badge>
      <BadgeIcon>
        <PackageIcon />
      </BadgeIcon>
      <span>Up to date</span>
      <BadgeStatus className="bg-emerald-500" />
    </Badge>
  );
}

Dependencies

Source Code

'use client';

import type { VariantProps } from 'cva';

import { Slot } from '@/components/slot';
import { cn, cva } from '@/lib/utils/classnames';

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 = ({
  children,
  className,
  ...rest
}: React.ComponentPropsWithRef<'div'>) => {
  return (
    <div
      data-badge-icon
      className={cn('flex items-center justify-center', className)}
      {...rest}
    >
      {children}
    </div>
  );
};

const BadgeStatus = ({
  className,
  ...rest
}: Omit<React.ComponentPropsWithRef<'div'>, 'children'>) => {
  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.

Prop Default Type
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

Neutral
Success
Error
Warning
Info
import { Badge } from '@/components/badge';

export default function BadgeVariantsPreview() {
  return (
    <div className="flex flex-wrap items-center gap-2">
      <Badge variant="neutral">Neutral</Badge>
      <Badge variant="success">Success</Badge>
      <Badge variant="error">Error</Badge>
      <Badge variant="warning">Warning</Badge>
      <Badge variant="info">Info</Badge>
    </div>
  );
}

Sizes

Extra small
Small
Medium
import { Badge } from '@/components/badge';

export default function BadgeSizesPreview() {
  return (
    <div className="flex flex-wrap items-center gap-2">
      <Badge size="xs">Extra small</Badge>
      <Badge size="sm">Small</Badge>
      <Badge size="md">Medium</Badge>
    </div>
  );
}

With icon and status

Up to date
import { PackageIcon } from '@phosphor-icons/react/dist/ssr';

import { Badge, BadgeIcon, BadgeStatus } from '@/components/badge';

export default function BadgePreview() {
  return (
    <Badge>
      <BadgeIcon>
        <PackageIcon />
      </BadgeIcon>
      <span>Up to date</span>
      <BadgeStatus className="bg-emerald-500" />
    </Badge>
  );
}

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

Previous

Avatar

Next

Button