Skip to main content

Badge

A compact label component for status indicators, categories, and metadata. Supports 3 variants, 6 color schemes, dot indicators, icons, dismissible behavior, and animated enter/exit transitions powered by Framer Motion.

Live Preview

Interact with the badge component in real time. Adjust variant, color, size, and features to see changes instantly.

Badge Label
tsx
1<Badge2variant="subtle"3color="primary"4size="md"5>6Badge Label7</Badge>

Installation

Install the Aidash Components package using your preferred package manager.

$ pnpm add @aidash/components

Import

Import the Badge and BadgeGroup components into your file.

tsx
import { Badge, BadgeGroup } from '@/components/aidash/badge';

Usage

The simplest way to use Badge with default props.

DefaultSuccessSolid
tsx
1<Badge>Default</Badge>2<Badge color="success">Success</Badge>3<Badge variant="solid" color="primary">Solid</Badge>

Variants

Three visual variants for different emphasis levels and contexts.

solid

primaryaccentsuccesswarningerrorneutral

outline

primaryaccentsuccesswarningerrorneutral

subtle

primaryaccentsuccesswarningerrorneutral
tsx
1{/* Solid — high emphasis */}2<Badge variant="solid" color="primary">Primary</Badge>3<Badge variant="solid" color="success">Success</Badge>4 5{/* Outline — medium emphasis */}6<Badge variant="outline" color="warning">Warning</Badge>7<Badge variant="outline" color="error">Error</Badge>8 9{/* Subtle — low emphasis (default) */}10<Badge variant="subtle" color="accent">Accent</Badge>11<Badge variant="subtle" color="neutral">Neutral</Badge>
Tip
Use subtle for inline metadata, outline for secondary status, and solid when the badge needs to stand out clearly.

Sizes

Three sizes to fit different layout contexts and text hierarchies.

SmallMediumLarge
tsx
1<Badge size="sm">Small</Badge>2<Badge size="md">Medium</Badge>3<Badge size="lg">Large</Badge>

Colors

Six semantic color schemes for status, categories, and emphasis.

primaryaccentsuccesswarningerrorneutral
primaryaccentsuccesswarningerrorneutral
tsx
1<Badge color="primary">Primary</Badge>2<Badge color="accent">Accent</Badge>3<Badge color="success">Success</Badge>4<Badge color="warning">Warning</Badge>5<Badge color="error">Error</Badge>6<Badge color="neutral">Neutral</Badge>

Features

Advanced capabilities including dot indicators, icons, dismissible behavior, and grouping.

Dot Indicator

OnlineAwayBusyOffline
tsx
1<Badge dot color="success">Online</Badge>2<Badge dot color="warning">Away</Badge>3<Badge dot color="error">Busy</Badge>4<Badge dot color="neutral">Offline</Badge>

Dismissible

ReactTypeScriptTailwindDraft
tsx
1<Badge dismissible color="primary">React</Badge>2<Badge dismissible color="accent">TypeScript</Badge>3 4{/* With callback */}5<Badge dismissible onDismiss={() => console.log('dismissed')}>6Removable7</Badge>

With Icon

FeaturedVerifiedUrgentFastSecure
tsx
1<Badge icon={<StarIcon />} color="warning" variant="solid">Featured</Badge>2<Badge icon={<CheckIcon />} color="success">Verified</Badge>3<Badge icon={<AlertIcon />} color="error" variant="outline">Urgent</Badge>

BadgeGroup

Gap: sm

ReactNext.jsTypeScript

Gap: md (default)

ReactNext.jsTypeScript

Gap: lg

ReactNext.jsTypeScript
tsx
1<BadgeGroup gap="sm">2<Badge color="primary" size="sm">React</Badge>3<Badge color="accent" size="sm">Next.js</Badge>4<Badge color="success" size="sm">TypeScript</Badge>5</BadgeGroup>6 7<BadgeGroup>8<Badge color="primary">React</Badge>9<Badge color="accent">Next.js</Badge>10<Badge color="success">TypeScript</Badge>11</BadgeGroup>

Accessibility

Built-in accessibility features for inclusive user experiences.

Semantic Markup

Renders as a <span> element, appropriate for inline labeling. Screen readers will read the badge text content naturally within the document flow.

Dismiss Button

The dismiss button includes aria-label="Dismiss" for screen readers. It is keyboard accessible with visible focus-visible ring styling.

Color Contrast

All color and variant combinations are designed to meet WCAG AA contrast requirements. The solid variant uses white text on dark backgrounds; subtle and outline variants use appropriately contrasting text colors.

Motion Preferences

Animations are powered by Framer Motion which respects the prefers-reduced-motion media query. Users who prefer reduced motion will see instant transitions.

tsx
1{/* Badge text is read naturally by screen readers */}2<Badge color="success" dot>Active</Badge>3 4{/* Dismissible badges have accessible dismiss buttons */}5<Badge dismissible onDismiss={() => removeTag('react')}>6React7</Badge>8 9{/* Use aria-label on parent for additional context */}10<div aria-label="Project tags">11<BadgeGroup>12  <Badge>Frontend</Badge>13  <Badge>TypeScript</Badge>14</BadgeGroup>15</div>

API Reference

Complete list of props accepted by Badge and BadgeGroup.

Badge
PropTypeDefaultDescription
childrenReactNodeBadge label content
variant'solid' | 'outline' | 'subtle''subtle'Visual style of the badge
color'primary' | 'accent' | 'success' | 'warning' | 'error' | 'neutral''primary'Color scheme of the badge
size'sm' | 'md' | 'lg''md'Size of the badge
dismissiblebooleanfalseShow a dismiss button to remove the badge
onDismiss() => voidCallback when the dismiss button is clicked
iconReactNodeIcon rendered before the label
dotbooleanfalseShow a colored dot indicator before the label
classNamestring''Additional CSS classes
BadgeGroup
PropTypeDefaultDescription
childrenReactNodeBadgeGroup content (Badge elements)
gap'sm' | 'md' | 'lg''md'Gap between badges
classNamestring''Additional CSS classes
Note
The Badge component uses AnimatePresence internally for smooth exit animations when dismissed. Each Badge manages its own visibility state, so dismissed badges animate out automatically.

Examples

Realistic production examples showing badges in context.

Status Indicators

API GatewayOperational
Database ClusterDegraded
CDN Edge NodesOutage
Background WorkersMaintenance
tsx
1<div className="flex items-center justify-between">2<span>API Gateway</span>3<Badge dot color="success" size="sm">Operational</Badge>4</div>5<div className="flex items-center justify-between">6<span>Database Cluster</span>7<Badge dot color="warning" size="sm">Degraded</Badge>8</div>

Notification Counts

Messages12
Alerts3
Updates7
Tasks24
tsx
1<div className="flex items-center gap-2">2<span>Messages</span>3<Badge variant="solid" color="primary" size="sm">12</Badge>4</div>5<div className="flex items-center gap-2">6<span>Alerts</span>7<Badge variant="solid" color="error" size="sm">3</Badge>8</div>

Tag Lists

Project Tags

FrontendDesign SystemOpen SourceIn Progress
tsx
1<BadgeGroup>2<Badge icon={<TagIcon />} color="primary" dismissible>Frontend</Badge>3<Badge icon={<TagIcon />} color="accent" dismissible>Design System</Badge>4<Badge icon={<TagIcon />} color="success" dismissible>Open Source</Badge>5<Badge icon={<TagIcon />} color="warning" dismissible>In Progress</Badge>6</BadgeGroup>

User Roles

AD

Admin User

admin@example.com

Admin
ED

Editor User

editor@example.com

Editor
VW

Viewer User

viewer@example.com

Viewer
tsx
1<Badge icon={<ShieldIcon />} variant="solid" color="primary" size="sm">2Admin3</Badge>4<Badge variant="outline" color="success" size="sm">Editor</Badge>5<Badge variant="subtle" color="neutral" size="sm">Viewer</Badge>

Best Practices

Guidelines for using badges effectively in your interface.

Do
  • Use consistent color semantics (green for success, red for error, etc.)
  • Keep badge labels short and scannable (1-2 words)
  • Use dot indicators for real-time status (online/offline)
  • Use BadgeGroup to wrap multiple related badges for proper spacing
  • Use subtle variant for metadata that should not draw too much attention
  • Provide onDismiss callback for dismissible badges to update state
Don't
  • Use badges for long text or full sentences
  • Mix different badge sizes within the same group
  • Use success color for non-positive statuses or error color for non-critical items
  • Overload a view with too many solid badges competing for attention
  • Use badges as interactive elements (use buttons instead)
  • Forget to handle the onDismiss callback when using dismissible badges

Source Code

Full source code for the Badge and BadgeGroup components.

src/components/aidash/badge.tsx
tsx
'use client';

import { motion, AnimatePresence } from 'framer-motion';
import { useState, type ReactNode } from 'react';
import { HugeiconsIcon } from '@hugeicons/react';
import { Cancel01Icon } from '@hugeicons/core-free-icons';

export type BadgeVariant = 'solid' | 'outline' | 'subtle';
export type BadgeColor = 'primary' | 'accent' | 'success' | 'warning' | 'error' | 'neutral';
export type BadgeSize = 'sm' | 'md' | 'lg';

export interface BadgeProps {
  children: ReactNode;
  variant?: BadgeVariant;
  color?: BadgeColor;
  size?: BadgeSize;
  dismissible?: boolean;
  onDismiss?: () => void;
  icon?: ReactNode;
  dot?: boolean;
  className?: string;
}

const colorMap: Record<BadgeColor, Record<BadgeVariant, string>> = {
  primary: {
    solid: 'bg-brand-500 text-white',
    outline: 'bg-transparent border border-brand-500 text-brand-500',
    subtle: 'bg-brand-500/10 text-brand-500',
  },
  accent: {
    solid: 'bg-brand-300 text-white',
    outline: 'bg-transparent border border-brand-300 text-brand-400',
    subtle: 'bg-brand-300/10 text-brand-400',
  },
  success: {
    solid: 'bg-success-600 text-white dark:bg-success-500',
    outline: 'bg-transparent border border-success-500 text-success-600 dark:text-success-400',
    subtle: 'bg-success-50 text-success-700 dark:bg-success-500/10 dark:text-success-400',
  },
  warning: {
    solid: 'bg-warn-500 text-white',
    outline: 'bg-transparent border border-warn-500 text-warn-600 dark:text-warn-400',
    subtle: 'bg-warn-50 text-warn-700 dark:bg-warn-500/10 dark:text-warn-400',
  },
  error: {
    solid: 'bg-danger-600 text-white dark:bg-danger-500',
    outline: 'bg-transparent border border-danger-500 text-danger-600 dark:text-danger-400',
    subtle: 'bg-danger-50 text-danger-700 dark:bg-danger-500/10 dark:text-danger-400',
  },
  neutral: {
    solid: 'bg-neutral-500 text-white',
    outline: 'bg-transparent border border-(--border) text-(--text-muted)',
    subtle: 'bg-neutral-100 text-neutral-500 dark:bg-neutral-500/10 dark:text-neutral-400',
  },
};

const sizeStyles: Record<BadgeSize, string> = {
  sm: 'text-[10px] px-2 py-0.5 gap-1',
  md: 'text-[10px] px-2.5 py-0.5 gap-1.5',
  lg: 'text-xs px-3 py-1 gap-1.5',
};

const dotSizes: Record<BadgeSize, string> = {
  sm: 'w-1.5 h-1.5',
  md: 'w-2 h-2',
  lg: 'w-2.5 h-2.5',
};

const dotColors: Record<BadgeColor, string> = {
  primary: 'bg-brand-500',
  accent: 'bg-brand-300',
  success: 'bg-success-500',
  warning: 'bg-warn-500',
  error: 'bg-danger-500',
  neutral: 'bg-neutral-400',
};

export function Badge({
  children,
  variant = 'subtle',
  color = 'primary',
  size = 'md',
  dismissible = false,
  onDismiss,
  icon,
  dot = false,
  className = '',
}: BadgeProps) {
  const [visible, setVisible] = useState(true);

  const handleDismiss = () => {
    setVisible(false);
    onDismiss?.();
  };

  return (
    <AnimatePresence>
      {visible && (
        <motion.span
          initial={{ opacity: 0, scale: 0.85 }}
          animate={{ opacity: 1, scale: 1 }}
          exit={{ opacity: 0, scale: 0.85 }}
          transition={{ duration: 0.2, ease: [0.2, 0, 0, 1] }}
          className={`inline-flex items-center font-medium rounded-lg whitespace-nowrap capitalize ${colorMap[color][variant]} ${sizeStyles[size]} ${className}`}
        >
          {dot && (
            <span aria-hidden="true" className={`rounded-full shrink-0 ${dotSizes[size]} ${variant === 'solid' ? 'bg-white/70' : dotColors[color]}`} />
          )}
          {icon && <span aria-hidden="true" className="shrink-0 [&>svg]:w-3.5 [&>svg]:h-3.5">{icon}</span>}
          {children}
          {dismissible && (
            <button
              type="button"
              onClick={handleDismiss}
              className="shrink-0 ml-0.5 rounded p-0.5 hover:bg-black/10 transition-colors focus:outline-none focus-visible:ring-1 focus-visible:ring-current"
              aria-label="Dismiss"
            >
              <HugeiconsIcon icon={Cancel01Icon} size={10} aria-hidden="true" />
            </button>
          )}
        </motion.span>
      )}
    </AnimatePresence>
  );
}

export interface BadgeGroupProps {
  children: ReactNode;
  gap?: 'sm' | 'md' | 'lg';
  className?: string;
}

const gapStyles: Record<string, string> = {
  sm: 'gap-1',
  md: 'gap-2',
  lg: 'gap-3',
};

export function BadgeGroup({ children, gap = 'md', className = '' }: BadgeGroupProps) {
  return (
    <div className={`flex flex-wrap items-center ${gapStyles[gap]} ${className}`}>
      {children}
    </div>
  );
}

Other components that work well alongside Badge.