March 14, 2025
O. Wolfson
When building React components with Tailwind CSS, you often need to:
Manually handling these cases can be messy. Thatβs where cn comes in.
cn?cn is a helper function that:
className props to override defaults, giving users full control over styles.Though cn is commonly used in shadcn/ui, it's a general-purpose utility that you can use in any React + Tailwind project.
cn Works InternallyA typical cn function is implemented like this:
tsximport { clsx } from "clsx";
import { twMerge } from "tailwind-merge";
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}
clsx(inputs): Combines and filters out false, null, or undefined values.twMerge(...): Ensures conflicting Tailwind classes are resolved, with later ones taking priority.Now, letβs see why this is useful.
cnImagine a Button component:
tsxexport function Button({ className }: { className?: string }) {
return (
<button className={`px-4 py-2 bg-blue-500 text-white ${className}`}>
Click Me
</button>
);
}
π΄ Issue:
<Button className="bg-red-500" />, the button will have both bg-blue-500 and bg-red-500, potentially causing conflicts.cn to Fix Ittsximport { cn } from "@/lib/utils"; // Assuming you've placed `cn` in `lib/utils.ts`
export function Button({ className }: { className?: string }) {
return (
<button className={cn("px-4 py-2 bg-blue-500 text-white", className)}>
Click Me
</button>
);
}
Now, if someone does:
tsx<Button className="bg-red-500 p-2" />
The final result will be:
html<button class="bg-red-500 text-white p-2">Click Me</button>
bg-red-500 replaces bg-blue-500p-2 replaces p-4We can take this further by using props to conditionally apply styles.
Card Component with Conditional Stylingtsxexport function Card({
className,
isHighlighted,
hasShadow,
}: {
className?: string;
isHighlighted?: boolean;
hasShadow?: boolean;
}) {
return (
<div
className={cn(
"p-4 border rounded",
isHighlighted && "bg-yellow-200",
hasShadow && "shadow-lg",
className
)}
>
Card Content
</div>
);
}
tsx<Card isHighlighted hasShadow className="border-red-500" />
html<div class="p-4 border rounded bg-yellow-200 shadow-lg border-red-500">
Card Content
</div>
bg-yellow-200 added due to isHighlightedshadow-lg added due to hasShadowborder-red-500 replaces borderThis approach makes components more reusable and customizable without losing control over default styles.
cnUse cn whenever:
β You need to merge Tailwind classes dynamically.
β You want to allow className overrides safely.
β You need to conditionally apply styles based on props.
β You want to prevent class conflicts in reusable components.
cn isn't just for shadcn/uiβitβs useful for any React + Tailwind project.By mastering cn, you'll improve your workflow and make your React components more flexible and maintainable.