Build websites 10x faster with HextaUI Blocks — Learn more
UI/UI

Badge

A versatile badge component for displaying status, labels, or categories.

DefaultSecondaryOutlineGhostDestructive
<div className="flex gap-4 flex-wrap">
  <Badge>Default</Badge>
  <Badge variant="secondary">Secondary</Badge>
  <Badge variant="outline">Outline</Badge>
  <Badge variant="ghost">Ghost</Badge>
  <Badge variant="destructive">Destructive</Badge>
</div>

Installation

Install following dependencies:

npm install class-variance-authority lucide-react
pnpm add class-variance-authority lucide-react
yarn add class-variance-authority lucide-react
bun add class-variance-authority lucide-react

Copy and paste the following code into your project.

components/ui/badge.tsx
"use client";

import * as React from "react";
import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "@/lib/utils";
import { type LucideIcon } from "lucide-react";

const badgeVariants = cva(
  "inline-flex items-center justify-center rounded-md border text-xs font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
  {
    variants: {
      variant: {
        default:
          "border-transparent bg-[hsl(var(--hu-primary))] text-[hsl(var(--hu-primary-foreground))] hover:bg-[hsl(var(--hu-primary))]/80 focus-visible:ring-[hsl(var(--hu-ring))]",
        secondary:
          "border-transparent bg-[hsl(var(--hu-secondary))] text-[hsl(var(--hu-secondary-foreground))] hover:bg-[hsl(var(--hu-secondary))]/80 focus-visible:ring-[hsl(var(--hu-ring))]",
        destructive:
          "border-transparent bg-[hsl(var(--hu-destructive))] text-[hsl(var(--hu-destructive-foreground))] hover:bg-[hsl(var(--hu-destructive))]/80 focus-visible:ring-[hsl(var(--hu-destructive))]",
        outline:
          "border-[hsl(var(--hu-border))] text-[hsl(var(--hu-foreground))] hover:bg-[hsl(var(--hu-accent))] hover:text-[hsl(var(--hu-accent-foreground))] focus-visible:ring-[hsl(var(--hu-ring))]",
        ghost:
          "border-transparent text-[hsl(var(--hu-foreground))] hover:bg-[hsl(var(--hu-accent))] hover:text-[hsl(var(--hu-accent-foreground))] focus-visible:ring-[hsl(var(--hu-ring))]",
      },
      size: {
        sm: "h-5 px-2 gap-1",
        default: "h-6 px-2.5 gap-1.5",
        lg: "h-7 px-3 text-sm gap-1.5",
      },
    },
    defaultVariants: {
      variant: "default",
      size: "default",
    },
  },
);

export interface BadgeProps
  extends React.HTMLAttributes<HTMLSpanElement>,
    VariantProps<typeof badgeVariants> {
  icon?: LucideIcon;
  iconPosition?: "left" | "right";
}

function Badge({
  className,
  variant,
  size,
  icon: Icon,
  iconPosition = "left",
  children,
  ...props
}: BadgeProps) {
  const iconSize = size === "sm" ? 12 : size === "lg" ? 14 : 12;

  return (
    <span
      className={cn(badgeVariants({ variant, size }), className)}
      {...props}
    >
      {Icon && iconPosition === "left" && (
        <Icon size={iconSize} className="shrink-0" />
      )}
      {children}
      {Icon && iconPosition === "right" && (
        <Icon size={iconSize} className="shrink-0" />
      )}
    </span>
  );
}

export { Badge, badgeVariants };
npx hextaui@latest add badge
pnpm dlx hextaui@latest add badge
yarn dlx hextaui@latest add badge
bun x hextaui@latest add badge

Usage

import { Badge } from "@/components/ui/Badge/Badge";
<Badge variant="default">Default</Badge>
<Badge variant="secondary">Secondary</Badge>
<Badge variant="outline">Outline</Badge>
<Badge variant="ghost">Ghost</Badge>
<Badge variant="destructive">Destructive</Badge>

Examples

Variants

DefaultSecondaryOutlineGhostDestructive
<div className="flex gap-4 flex-wrap">
  <Badge variant="default">Default</Badge>
  <Badge variant="secondary">Secondary</Badge>
  <Badge variant="outline">Outline</Badge>
  <Badge variant="ghost">Ghost</Badge>
  <Badge variant="destructive">Destructive</Badge>
</div>

Sizes

SmallDefaultLarge
<div className="flex gap-4 flex-wrap items-center">
  <Badge size="sm">Small</Badge>
  <Badge size="default">Default</Badge>
  <Badge size="lg">Large</Badge>
</div>

Status Indicators

DraftIn ProgressCompletedCancelled
<div className="flex gap-4 flex-wrap">
  <Badge variant="ghost">Draft</Badge>
  <Badge variant="secondary">In Progress</Badge>
  <Badge variant="default">Completed</Badge>
  <Badge variant="destructive">Cancelled</Badge>
</div>

Numbers and Counts

1

99+

5

New
<div className="flex gap-4 flex-wrap items-center">
  <Badge variant="outline" size="sm">
    1
  </Badge>
  <Badge variant="secondary">99+</Badge>
  <Badge variant="destructive" size="sm">
    5
  </Badge>
  <Badge variant="default">New</Badge>
</div>

Custom Styling

Rounded

Premium

Dashed

Bold
<div className="flex gap-4 flex-wrap">
  <Badge className="rounded-full">Rounded</Badge>
  <Badge className="uppercase tracking-wider" size="sm">
    Premium
  </Badge>
  <Badge variant="outline" className="border-dashed">
    Dashed
  </Badge>
  <Badge className="font-bold">Bold</Badge>
</div>

With Icons

Featured

Verified

Warning

Premium

Continue

import { Star, CheckCircle, AlertTriangle, Zap, ArrowRight } from "lucide-react";

<div className="flex gap-4 flex-wrap">
  <Badge icon={Star} variant="default">
    Featured
  </Badge>
  <Badge icon={CheckCircle} variant="secondary" iconPosition="left">
    Verified
  </Badge>
  <Badge icon={AlertTriangle} variant="destructive">
    Warning
  </Badge>
  <Badge icon={Zap} variant="outline">
    Premium
  </Badge>
  <Badge icon={ArrowRight} variant="ghost" iconPosition="right">
    Continue
  </Badge>
</div>

Icon Sizes

Small

Default

Large

import { Heart } from "lucide-react";

<div className="flex gap-4 flex-wrap items-center">
  <Badge icon={Heart} variant="default" size="sm">
    Small
  </Badge>
  <Badge icon={Heart} variant="secondary" size="default">
    Default
  </Badge>
  <Badge icon={Heart} variant="outline" size="lg">
    Large
  </Badge>
</div>

Interactive Badges

Clickable

Remove

Focusable

<div className="flex gap-4 flex-wrap">
  <Badge className="cursor-pointer" onClick={() => alert("Badge clicked!")}>
    Clickable
  </Badge>
  <Badge
    variant="outline"
    className="cursor-pointer hover:border-red-500 hover:text-red-500"
  >
    Remove
  </Badge>
  <Badge variant="secondary" className="cursor-pointer" tabIndex={0}>
    Focusable
  </Badge>
</div>

Real-world Examples

Article:Published

React

Tutorial

User:Premium

Admin

Issue:Critical

Bug

P1

<div className="space-y-4">
  <div className="flex items-center gap-2">
    <span className="text-sm">Article:</span>
    <Badge variant="default">Published</Badge>
    <Badge variant="secondary" size="sm">
      React
    </Badge>
    <Badge variant="outline" size="sm">
      Tutorial
    </Badge>
  </div>
  <div className="flex items-center gap-2">
    <span className="text-sm">User:</span>
    <Badge variant="secondary">Premium</Badge>
    <Badge variant="ghost" size="sm">
      Admin
    </Badge>
  </div>
  <div className="flex items-center gap-2">
    <span className="text-sm">Issue:</span>
    <Badge variant="destructive">Critical</Badge>
    <Badge variant="outline" size="sm">
      Bug
    </Badge>
    <Badge variant="ghost" size="sm">
      P1
    </Badge>
  </div>
</div>

Props

PropTypeDefault
children?
ReactNode
undefined
className?
string
undefined
iconPosition?
"left" | "right"
"left"
icon?
LucideIcon
undefined
size?
"sm" | "default" | "lg"
"default"
variant?
"default" | "secondary" | "destructive" | "outline" | "ghost"
"default"
Edit on GitHub

Last updated on