---
title: Badge
description: Compact labels for status, tags, and metadata.
group: Components
order: 16
---

Add a compact label to statuses, filters, and small pieces of metadata. Use text alone or pair it with an icon or spinner.

```tsx
import { Badge } from "@/components/ui/badge"

export function Example() {
  return (
    <>
      <Badge>Stable</Badge>
      <Badge variant="secondary">Beta</Badge>
      <Badge variant="destructive">Broken</Badge>
      <Badge variant="outline">Draft</Badge>
    </>
  )
}
```

## Installation

```bash
npx @arctis-sh/@arctis-sh/ui@latest add badge
```

## Usage

```tsx
import { Badge } from "@/components/ui/badge"
```

```tsx
<Badge>Stable</Badge>
```

```tsx
<Badge variant="default | secondary | destructive | outline | ghost">
  Stable
</Badge>
```

## Variants

Choose a `variant` to set the emphasis. Default is solid, secondary uses a softer surface, destructive marks problems, and outline or ghost stays quiet.

```tsx
import { Badge } from "@/components/ui/badge"

export function Example() {
  return (
    <>
      <Badge>Stable</Badge>
      <Badge variant="secondary">Beta</Badge>
      <Badge variant="destructive">Broken</Badge>
      <Badge variant="outline">Draft</Badge>
      <Badge variant="ghost">Hint</Badge>
    </>
  )
}
```

## With icon

Place an SVG inside the badge. Add `data-icon="inline-start"` or `data-icon="inline-end"` to adjust the spacing on that side.

```tsx
import { Badge } from "@/components/ui/badge"

export function Example() {
  return (
    <>
      <Badge variant="secondary">
        <CheckBadgeIcon data-icon="inline-start" />
        Synced
      </Badge>
      <Badge variant="outline">
        Saved
        <BookmarkIcon data-icon="inline-end" />
      </Badge>
    </>
  )
}
```

## With spinner

Use the same icon slot for a spinner while work is in progress. Keep `data-icon` so the spacing stays balanced.

```tsx
import { Badge } from "@/components/ui/badge"

export function Example() {
  return (
    <>
      <Badge variant="destructive">
        <SpinnerIcon data-icon="inline-start" />
        Removing
      </Badge>
      <Badge variant="secondary">
        Building
        <SpinnerIcon data-icon="inline-end" />
      </Badge>
    </>
  )
}
```

## Link

Wrap the badge in an anchor when it should navigate. The badge provides the visual treatment; the link remains the interactive element.

```tsx
import { Badge } from "@/components/ui/badge"

export function Example() {
  return (
    <a href="#link" className="inline-flex">
      <Badge>
        Link section
        <ArrowUpRightIcon data-icon="inline-end" />
      </Badge>
    </a>
  )
}
```

## Custom colors

Use utility classes to give a specific status its own fill and text color.

```tsx
import { Badge } from "@/components/ui/badge"

export function Example() {
  return (
    <>
      <Badge className="border-transparent bg-blue-600 text-white">
        Docs
      </Badge>
      <Badge className="border-transparent bg-success text-success-foreground">
        Live
      </Badge>
      <Badge className="border-transparent bg-sky-600 text-white">
        Preview
      </Badge>
      <Badge className="border-transparent bg-violet-600 text-white">
        Internal
      </Badge>
      <Badge className="border-transparent bg-destructive text-destructive-foreground">
        Blocked
      </Badge>
    </>
  )
}
```

## API Reference

### Badge

| Prop | Type | Default |
| --- | --- | --- |
| variant | "default" \| "secondary" \| "destructive" \| "outline" \| "ghost" | "default" |
| className | string | — |

```tsx
<Badge variant="secondary">New</Badge>
```

## Source

```tsx
import type { HTMLAttributes, ReactNode } from "react";
import { cn } from "@/lib/utils";

type BadgeVariant =
  | "default"
  | "secondary"
  | "destructive"
  | "outline"
  | "ghost";

type BadgeProps = HTMLAttributes<HTMLSpanElement> & {
  variant?: BadgeVariant;
  children: ReactNode;
};

const variantClass: Record<BadgeVariant, string> = {
  default: "border-transparent bg-primary text-primary-foreground",
  secondary:
    "border-transparent bg-secondary text-secondary-foreground",
  destructive:
    "border-transparent bg-destructive text-destructive-foreground",
  outline: "border-border bg-transparent text-foreground",
  ghost:
    "border-transparent bg-transparent text-muted-foreground hover:bg-accent hover:text-accent-foreground",
};

export function Badge({
  variant = "default",
  className,
  children,
  ...props
}: BadgeProps) {
  const hasBgOverride = /(?:^|\s)(?:!)?bg-/.test(className ?? "");

  return (
    <span
      data-slot="badge"
      data-variant={variant}
      className={cn(
        "inline-flex h-5 w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-md border px-2 text-[11px] font-medium tracking-wide whitespace-nowrap",
        "has-[[data-icon=inline-start]]:pl-1.5 has-[[data-icon=inline-end]]:pr-1.5",
        "[&>svg]:pointer-events-none [&>svg]:size-3",
        hasBgOverride ? "border-transparent" : variantClass[variant],
        className,
      )}
      {...props}
    >
      {children}
    </span>
  );
}
```
