UI/UI
Chip
A compact, interactive element for displaying status, categories, or filters with optional icons and dismissible functionality.
Default
Secondary
Outline
Ghost
Destructive
<div className="flex gap-4 flex-wrap">
<Chip>Default</Chip>
<Chip variant="secondary">Secondary</Chip>
<Chip variant="outline">Outline</Chip>
<Chip variant="ghost">Ghost</Chip>
<Chip variant="destructive">Destructive</Chip>
</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.
"use client";
import * as React from "react";
import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "@/lib/utils";
import { type LucideIcon, X } from "lucide-react";
const chipVariants = cva(
"inline-flex items-center justify-center rounded-full 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-6 px-2 gap-1 text-xs",
default: "h-7 px-3 gap-1.5 text-sm",
lg: "h-8 px-4 text-sm gap-2",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
},
);
export interface ChipProps
extends React.HTMLAttributes<HTMLDivElement>,
VariantProps<typeof chipVariants> {
icon?: LucideIcon;
iconPosition?: "left" | "right";
dismissible?: boolean;
onDismiss?: () => void;
}
const Chip = React.forwardRef<HTMLDivElement, ChipProps>(
(
{
className,
variant,
size,
icon: Icon,
iconPosition = "left",
dismissible = false,
onDismiss,
children,
...props
},
ref,
) => {
const iconSize = size === "sm" ? 12 : size === "lg" ? 14 : 12;
const closeIconSize = size === "sm" ? 10 : size === "lg" ? 12 : 10;
const handleDismiss = (e: React.MouseEvent) => {
e.stopPropagation();
onDismiss?.();
};
return (
<div
ref={ref}
className={cn(chipVariants({ variant, size }), className)}
{...props}
>
{Icon && iconPosition === "left" && (
<Icon size={iconSize} className="shrink-0" />
)}
{children}
{Icon && iconPosition === "right" && !dismissible && (
<Icon size={iconSize} className="shrink-0" />
)}
{dismissible && (
<button
type="button"
onClick={handleDismiss}
className="ml-1 shrink-0 rounded-full p-0.5 hover:bg-black/10 dark:hover:bg-white/10 transition-colors"
aria-label="Remove"
>
<X size={closeIconSize} />
</button>
)}
</div>
);
},
);
Chip.displayName = "Chip";
export { Chip, chipVariants };
npx hextaui@latest add chip
pnpm dlx hextaui@latest add chip
yarn dlx hextaui@latest add chip
bun x hextaui@latest add chip
Usage
import { Chip } from "@/components/ui/Chip";
<Chip variant="default">Default Chip</Chip>
<Chip variant="secondary">Secondary Chip</Chip>
<Chip variant="outline">Outline Chip</Chip>
<Chip variant="ghost">Ghost Chip</Chip>
<Chip variant="destructive">Destructive Chip</Chip>
Examples
Variants
Default
Secondary
Outline
Ghost
Destructive
<div className="flex gap-4 flex-wrap">
<Chip variant="default">Default</Chip>
<Chip variant="secondary">Secondary</Chip>
<Chip variant="outline">Outline</Chip>
<Chip variant="ghost">Ghost</Chip>
<Chip variant="destructive">Destructive</Chip>
</div>
Sizes
Small
Default
Large
<div className="flex gap-4 flex-wrap items-center">
<Chip size="sm">Small</Chip>
<Chip size="default">Default</Chip>
<Chip size="lg">Large</Chip>
</div>
With Icons
Category
Member
Featured
Premium
Warning
import { Tag, User, Star, Shield, AlertTriangle } from "lucide-react";
<div className="flex gap-4 flex-wrap">
<Chip icon={Tag} variant="default">
Category
</Chip>
<Chip icon={User} variant="secondary" iconPosition="left">
Member
</Chip>
<Chip icon={Star} variant="outline">
Featured
</Chip>
<Chip icon={Shield} variant="ghost">
Premium
</Chip>
<Chip icon={AlertTriangle} variant="destructive">
Warning
</Chip>
</div>
Icon Positions
Next
Continue
Call
import { ArrowRight, Mail, Phone } from "lucide-react";
<div className="flex gap-4 flex-wrap">
<Chip icon={ArrowRight} variant="default" iconPosition="left">
Next
</Chip>
<Chip icon={ArrowRight} variant="secondary" iconPosition="right">
Continue
</Chip>
<Chip icon={Mail} variant="outline" iconPosition="left">
Email
</Chip>
<Chip icon={Phone} variant="ghost" iconPosition="right">
Call
</Chip>
</div>
Dismissible Chips
Remove me
JavaScript
John Doe
Error
import { Tag, User } from "lucide-react";
<div className="flex gap-4 flex-wrap">
<Chip dismissible onDismiss={() => alert("Chip dismissed!")}>
Remove me
</Chip>
<Chip
icon={Tag}
variant="secondary"
dismissible
onDismiss={() => alert("Tag removed!")}
>
JavaScript
</Chip>
<Chip
icon={User}
variant="outline"
dismissible
onDismiss={() => alert("User removed!")}
>
John Doe
</Chip>
<Chip variant="destructive" dismissible size="sm">
Error
</Chip>
</div>
Filter Tags
Technologies
React
TypeScript
Next.js
Tailwind
Team Members
Alice
Bob
Charlie
import { Tag, User } from "lucide-react";
<div className="space-y-4">
<div>
<h4 className="text-sm font-medium mb-2">Technologies</h4>
<div className="flex gap-2 flex-wrap">
<Chip icon={Tag} variant="outline" dismissible>React</Chip>
<Chip icon={Tag} variant="outline" dismissible>TypeScript</Chip>
<Chip icon={Tag} variant="outline" dismissible>Next.js</Chip>
<Chip icon={Tag} variant="outline" dismissible>Tailwind</Chip>
</div>
</div>
<div>
<h4 className="text-sm font-medium mb-2">Team Members</h4>
<div className="flex gap-2 flex-wrap">
<Chip icon={User} variant="secondary" size="sm" dismissible>Alice</Chip>
<Chip icon={User} variant="secondary" size="sm" dismissible>Bob</Chip>
<Chip icon={User} variant="secondary" size="sm" dismissible>Charlie</Chip>
</div>
</div>
</div>
Status Indicators
Pending
In Progress
Completed
Failed
import { Clock, CheckCircle, Check, X } from "lucide-react";
<div className="flex gap-4 flex-wrap">
<Chip icon={Clock} variant="ghost">Pending</Chip>
<Chip icon={CheckCircle} variant="secondary">In Progress</Chip>
<Chip icon={Check} variant="default">Completed</Chip>
<Chip icon={X} variant="destructive">Failed</Chip>
</div>
Contact Information
+1 (555) 123-4567
San Francisco, CA
import { Mail, Phone, MapPin, Globe } from "lucide-react";
<div className="space-y-3">
<div className="flex gap-2 flex-wrap">
<Chip icon={Mail} variant="outline" size="sm">
john@example.com
</Chip>
<Chip icon={Phone} variant="outline" size="sm">
+1 (555) 123-4567
</Chip>
</div>
<div className="flex gap-2 flex-wrap">
<Chip icon={MapPin} variant="ghost" size="sm">
San Francisco, CA
</Chip>
<Chip icon={Globe} variant="ghost" size="sm">
www.example.com
</Chip>
</div>
</div>
Interactive Chips
Clickable
Like
Focusable
Add Filter
import { Heart, Plus } from "lucide-react";
<div className="flex gap-4 flex-wrap">
<Chip className="cursor-pointer" onClick={() => alert("Chip clicked!")}>
Clickable
</Chip>
<Chip
icon={Heart}
variant="outline"
className="cursor-pointer hover:bg-red-50 hover:border-red-300 hover:text-red-600"
onClick={() => alert("Like clicked!")}
>
Like
</Chip>
<Chip variant="secondary" className="cursor-pointer" tabIndex={0}>
Focusable
</Chip>
<Chip
icon={Plus}
variant="ghost"
className="cursor-pointer"
onClick={() => alert("Add clicked!")}
>
Add Filter
</Chip>
</div>
Real-world Examples
Product Tags
New
Bestseller
Sale
Limited
Selected Filters
Price: $50-100
Brand: Nike
Color: Blue
Size: Medium
Event Attendees
Sarah Wilson
Mike Johnson
Anna Davis
+5 more
import { Award, Star, Tag, User } from "lucide-react";
<div className="space-y-6">
<div>
<h4 className="text-sm font-medium mb-3">Product Tags</h4>
<div className="flex gap-2 flex-wrap">
<Chip icon={Award} variant="default" size="sm">New</Chip>
<Chip icon={Star} variant="secondary" size="sm">Bestseller</Chip>
<Chip icon={Tag} variant="outline" size="sm">Sale</Chip>
<Chip variant="destructive" size="sm">Limited</Chip>
</div>
</div>
<div>
<h4 className="text-sm font-medium mb-3">Selected Filters</h4>
<div className="flex gap-2 flex-wrap">
<Chip variant="outline" dismissible>Price: $50-100</Chip>
<Chip variant="outline" dismissible>Brand: Nike</Chip>
<Chip variant="outline" dismissible>Color: Blue</Chip>
<Chip variant="outline" dismissible>Size: Medium</Chip>
</div>
</div>
<div>
<h4 className="text-sm font-medium mb-3">Event Attendees</h4>
<div className="flex gap-2 flex-wrap">
<Chip icon={User} variant="secondary" size="sm" dismissible>
Sarah Wilson
</Chip>
<Chip icon={User} variant="secondary" size="sm" dismissible>
Mike Johnson
</Chip>
<Chip icon={User} variant="secondary" size="sm" dismissible>
Anna Davis
</Chip>
<Chip variant="ghost" size="sm" className="cursor-pointer">
+5 more
</Chip>
</div>
</div>
</div>
Props
Prop | Type | Default |
---|---|---|
children? | ReactNode | undefined |
className? | string | undefined |
onDismiss? | () => void | undefined |
dismissible? | boolean | false |
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