cn
Merge and dedupe Tailwind CSS classes intelligently
A utility function to merge Tailwind CSS classes intelligently, handling conflicts and deduplication.
Usage
import { cn } from "@/lib/utils";function Button({ className, variant = "default" }) {return (<buttonclassName={cn("px-4 py-2 rounded-md font-medium transition-colors",variant === "default" && "bg-blue-500 text-white hover:bg-blue-600",variant === "outline" && "border border-gray-300 hover:bg-gray-100",className)}>Click me</button>);}
Features
- Merges multiple class strings
- Handles conditional classes
- Resolves Tailwind CSS conflicts (last class wins)
- Removes duplicate classes
- Type-safe with TypeScript
Installation
This utility requires two dependencies:
pnpm install clsx tailwind-merge
API Reference
Parameters
...inputs(ClassValue[]) - Any number of class values (strings, objects, arrays)
Returns
Returns a merged string of deduplicated Tailwind classes.
Implementation
import { type ClassValue, clsx } from "clsx";import { twMerge } from "tailwind-merge";export function cn(...inputs: ClassValue[]) {return twMerge(clsx(inputs));}
Examples
Basic Merging
cn("px-4 py-2", "bg-blue-500");// => 'px-4 py-2 bg-blue-500'
Handling Conflicts
// Last class wins for conflicting utilitiescn("bg-red-500", "bg-blue-500");// => 'bg-blue-500'cn("p-4", "px-8");// => 'py-4 px-8'
Conditional Classes
cn("base-class", condition && "conditional-class");// => 'base-class conditional-class' (if condition is true)// => 'base-class' (if condition is false)cn("text-sm", {"font-bold": isBold,"text-red-500": hasError,});
Component Variants
function Alert({ variant, className, children }) {return (<divclassName={cn("p-4 rounded-lg border",{"bg-blue-50 border-blue-200 text-blue-900": variant === "info","bg-green-50 border-green-200 text-green-900": variant === "success","bg-yellow-50 border-yellow-200 text-yellow-900":variant === "warning","bg-red-50 border-red-200 text-red-900": variant === "error",},className)}>{children}</div>);}
Size Variants
function Badge({ size = "md", className, children }) {return (<spanclassName={cn("inline-flex items-center rounded-full font-medium",{"px-2 py-0.5 text-xs": size === "sm","px-3 py-1 text-sm": size === "md","px-4 py-1.5 text-base": size === "lg",},className)}>{children}</span>);}
Complex Component
function Card({variant = "default",padding = "md",shadow = true,className,children,}) {return (<divclassName={cn("rounded-lg border border-gray-200",// Variants{"bg-white": variant === "default","bg-gray-50": variant === "muted","bg-linear-to-br from-blue-50 to-indigo-50": variant === "gradient",},// Padding{"p-3": padding === "sm","p-6": padding === "md","p-8": padding === "lg",},// Shadowshadow && "shadow-md hover:shadow-lg transition-shadow",className)}>{children}</div>);}
With Arrays
const baseStyles = ["flex", "items-center", "gap-2"];const variantStyles = isActive? ["bg-blue-500", "text-white"]: ["bg-gray-100"];cn(baseStyles, variantStyles, "hover:opacity-80");
Responsive Classes
function ResponsiveGrid({ className }) {return (<divclassName={cn("grid gap-4","grid-cols-1","md:grid-cols-2","lg:grid-cols-3","xl:grid-cols-4",className)}>{/* Content */}</div>);}