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.
Props & Styling
Props
| Prop | Type | Default | Description |
|---|---|---|---|
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 variantBadgeStyles — pill/monoPill are rounded-md, flat is rounded-sm. |
children | ReactNode | — | Badge label content. |
...rest | native div props | — | Spread 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.
variant: monoPill
same rounded-md shape, meant to pair with color: mono.
variant: flat
rounded-sm, a sharper edge for dense filter/tag lists.
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