Badge

Displays a badge or a component that looks like a badge.

DefaultSecondaryDestructive

Installation

Install the component Badge in your project using the CLI.

Badge.tsx

pnpm dlx behsseui@latest add Badge

Install the component manually.

Create a ui folder at the root of the project, then a component folder inside it, and finally a Badge.tsx file in that folder.

Copy and paste the following code into your project.

ui/components/Badge.tsx

1import type { HTMLAttributes, ReactNode } from "react";
2import { Slot, type AsChildProps } from "./internals/Slot";
3import { cva, type VariantProps } from "class-variance-authority"
4import { cn } from "@/lib/utils";
5
6const badgeVariants = cva(
7 "inline-flex items-center justify-center gap-1.5 rounded-full text-xs font-medium transition-colors whitespace-nowrap shrink-0 [&>svg]:pointer-events-none [&>svg]:shrink-0 [&>svg]:size-3 [&>svg]:mt-px",
8 {
9 variants: {
10 variant: {
11 default: "bg-primary text-primary-foreground",
12 secondary: "bg-secondary text-secondary-foreground",
13 destructive: "bg-destructive text-destructive-foreground",
14 outline: "border border-border bg-background text-foreground",
15 ghost: "hover:bg-accent hover:text-accent-foreground",
16 },
17 size: {
18 default: "h-6 px-2.5 py-0.5",
19 sm: "h-5 px-2 py-0.5 text-[10px]",
20 lg: "h-7 px-3 py-1",
21 },
22 },
23 defaultVariants: {
24 variant: "default",
25 size: "default",
26 },
27 }
28);
29
30type Props = AsChildProps<
31 {
32 children: ReactNode;
33 className?: string;
34 } & VariantProps<typeof badgeVariants>,
35 HTMLAttributes<HTMLSpanElement>
36>;
37
38export function Badge({
39 variant = "default",
40 size = "default",
41 children,
42 className,
43 asChild,
44 ...props
45}: Props) {
46 const Component = asChild ? Slot : "span";
47 return (
48 <Component className={cn(badgeVariants({ variant, size, className }))} {...props}>
49 {children}
50 </Component>
51 );
52}
53

For Badge.tsx to work, create an internals folder inside ui/component, then create a Slot.tsx file in it with the following code.

ui/components/internals/Slot.tsx

1import {
2 Children,
3 cloneElement,
4 isValidElement,
5 type HTMLAttributes,
6 type PropsWithChildren,
7 type ReactElement,
8 type ReactNode,
9} from "react";
10import { twMerge } from "tailwind-merge";
11
12// Type utilitaire pour simplifier le typage des composant supportant asChild
13export type AsChildProps<BaseProps, SecondaryProps = {}> =
14 | ({ asChild: true; children: ReactNode } & BaseProps)
15 | ({ asChild?: false; children?: ReactNode } & BaseProps & SecondaryProps);
16
17export function Slot(props: PropsWithChildren<HTMLAttributes<HTMLElement>>) {
18 const children = Children.toArray(props.children).filter((c) =>
19 isValidElement(c)
20 );
21
22 if (children.length !== 1) {
23 throw new Error("Slot must have exactly one child element");
24 }
25
26 const child = children[0] as ReactElement<HTMLAttributes<HTMLElement>>;
27
28 return cloneElement(child, {
29 ...props,
30 ...child.props,
31 style:
32 props.style || child.props.style
33 ? {
34 ...props.style,
35 ...child.props.style,
36 }
37 : undefined,
38 className:
39 props.className || child.props.className
40 ? twMerge(props.className, child.props.className)
41 : undefined,
42 });
43}

Usages

Different variants and use cases for the Badge component.

Variants

Different badge variants for various contexts.

DefaultSecondaryDestructiveOutlineGhost

Variants.tsx

<div className="flex items-center gap-2">
  <Badge>Default</Badge>
  <Badge variant="secondary">Secondary</Badge>
  <Badge variant="destructive">Destructive</Badge>
  <Badge variant="outline">Outline</Badge>
  <Badge variant="ghost">Ghost</Badge>
</div>

With Icons

Badges can include icons at the start or end.

SuccessError

With Icons.tsx

import Check from "@/ui/icons/Check"
import AlertCircle from "@/ui/icons/AlertCircle"

<div className="flex items-center gap-2">
  <Badge>
    <Check className="h-3 w-3" />
    Success
  </Badge>
  <Badge variant="destructive">
    Error
    <AlertCircle className="h-3 w-3" />
  </Badge>
</div>

AsChild

Use the asChild prop to render the badge as a link or other element.

AsChild.tsx

import ArrowUpRight from "@/ui/icons/ArrowUpRight"

<Badge asChild>
  <a href="https://ui.behsse.com">
    Link Badge
    <ArrowUpRight className="h-3 w-3" />
  </a>
</Badge>