Badge

3 variants of this component were created in order to address most scenarios I’ve seen badges used: pill, mono-pilled, and flat. Badges are either already included in the element or dynamically deleted / added (filters, tags, etc.) The badge component is a case where no interaction or animation is necessary. An animation would cause the element to stick out too much for its intended purpose and an interaction would cause the adding / deleting of badges to feel sluggish.

Default
Success
Error
Warning
Mono

Props & Styling

Props

PropTypeDefaultDescription
color'blue' | 'mono' | 'red' | 'green' | 'yellow'— (required)Selects the text/background/border token set from badgeColors.
variant'pill' | 'monoPill' | 'flat'— (required)Selects the corner radius from variantBadgeStylespill/monoPill are rounded-md, flat is rounded-sm.
childrenReactNodeBadge label content.
...restnative div propsSpread onto the root element (e.g. onClick, id).

Styling

The root className is built with cn('inline-flex items-center px-2 text-sm font-medium', badgeColors[color], variantBadgeStyles[variant]).

Caveat: {...rest} is spread after className on the JSX element, not merged into it. If you pass your own className through ...rest, it will fully replace the computed color/shape classes rather than combine with them — fork the component and route an extra className prop through cn() if you need to add utility classes alongside the built-in styling.

No motion is applied to Badge on purpose: it's meant to be glanced at (a status label) or added/removed in bulk (filters, tags), and animating it would make routine list updates feel sluggish.

Variants

variant: pill

rounded-md, the default shape for status pills.

Blue
Green
Red
Yellow

variant: monoPill

same rounded-md shape, meant to pair with color: mono.

Mono Pill

variant: flat

rounded-sm, a sharper edge for dense filter/tag lists.

Flat
Flat

Code

import React from 'react'
import { cn } from 'src/lib/utils';

const variantBadgeStyles = {
    pill: 'rounded-md',
    monoPill: 'rounded-md',
    flat: 'rounded-sm'
} as const;

const badgeColors = {
    blue: 'text-blue-500 bg-blue-100/60 border border-blue-300',
    mono: 'text-gray-500 bg-gray-100/60 border-gray-300',
    red: 'text-red-500 bg-red-100/60 border-red-300',
    green: 'text-green-500 bg-green-100/60 border-green-300',
    yellow: 'text-yellow-500 bg-yellow-100/60 border-yellow-300'
} as const;

interface BadgeProps extends React.ComponentPropsWithoutRef<'div'> {
    color: keyof typeof badgeColors
    variant: keyof typeof variantBadgeStyles
}

const Badge = ({color, variant, children, ...rest}: BadgeProps) => {
  return (
    <div 
    className = {
        cn('inline-flex items-center px-2 text-sm font-medium',
        badgeColors[color],
        variantBadgeStyles[variant]
        )}
    {...rest}    
    >
       {children}
    </div>
  )
}

export default Badge