Documentation Index
Fetch the complete documentation index at: https://mintlify.com/kokonut-labs/kokonutui/llms.txt
Use this file to discover all available pages before exploring further.
The cn() function is a utility that intelligently merges Tailwind CSS class names, resolving conflicts and removing duplicates. It combines clsx for conditional classes and tailwind-merge for Tailwind-specific merging.
Installation
The cn utility is essential for most Kokonut UI components. Install it via the CLI:
Tab Title
Tab Title
Tab Title
bunx --bun shadcn@latest add https://kokonutui.com/r/utils.json
npx shadcn@latest add https://kokonutui.com/r/utils.json
pnpm dlx shadcn@latest add https://kokonutui.com/r/utils.json
This will create lib/utils.ts in your project with the cn function.
Source Code
import { type ClassValue, clsx } from "clsx";
import { twMerge } from "tailwind-merge";
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}
Usage
import { cn } from "@/lib/utils";
function Button({ className, isActive }) {
return (
<button
className={cn(
"px-4 py-2 rounded bg-blue-500 text-white",
isActive && "bg-green-500",
className
)}
>
Click me
</button>
);
}
Why cn()?
Problem
When building reusable components with Tailwind CSS, you often need to:
- Apply base classes
- Add conditional classes
- Allow users to override classes
- Handle conflicting classes (e.g.,
bg-blue-500 vs bg-red-500)
Naive string concatenation doesn’t work:
// ❌ Both background colors will be applied!
<div className={`bg-blue-500 ${className}`} />
// If className="bg-red-500", both bg-blue-500 AND bg-red-500 are in the DOM
Solution
The cn() function:
- Merges classes intelligently using
tailwind-merge
- Handles conditionals using
clsx
- Resolves conflicts - Later classes override earlier ones
- Removes duplicates - Only keeps the last occurrence
Examples
Basic Merging
cn("px-2 py-1", "px-4") // "py-1 px-4"
// px-4 overrides px-2
Conditional Classes
cn(
"text-base",
isError && "text-red-500",
isSuccess && "text-green-500"
)
// Returns: "text-base text-red-500" if isError is true
Variant System
function Badge({ variant, className }) {
return (
<span
className={cn(
"px-2 py-1 rounded text-sm",
{
"bg-blue-100 text-blue-700": variant === "info",
"bg-red-100 text-red-700": variant === "error",
"bg-green-100 text-green-700": variant === "success",
},
className
)}
/>
);
}
Overriding Component Styles
function Card({ className, children }) {
return (
<div
className={cn(
"rounded-lg border bg-white p-6",
className // User can override any of these
)}
>
{children}
</div>
);
}
// Usage:
<Card className="bg-gray-100 p-4">
{/* bg-gray-100 overrides bg-white */}
{/* p-4 overrides p-6 */}
</Card>
Complex Conditional Logic
function Button({ size, variant, isLoading, disabled, className }) {
return (
<button
className={cn(
// Base styles
"rounded font-medium transition-colors",
// Size variants
{
"px-3 py-1.5 text-sm": size === "sm",
"px-4 py-2 text-base": size === "md",
"px-6 py-3 text-lg": size === "lg",
},
// Color variants
{
"bg-blue-500 text-white hover:bg-blue-600": variant === "primary",
"bg-gray-200 text-gray-900 hover:bg-gray-300": variant === "secondary",
},
// State styles
isLoading && "opacity-50 cursor-wait",
disabled && "opacity-50 cursor-not-allowed",
// User overrides
className
)}
/>
);
}
Array and Object Support
// Arrays
cn(["px-4", "py-2"], ["bg-blue-500"])
// Objects (keys with truthy values are included)
cn({
"text-red-500": isError,
"text-green-500": isSuccess,
"font-bold": true,
})
// Mixed
cn(
"base-class",
["array-class-1", "array-class-2"],
{ "conditional-class": condition },
className
)
Best Practices
1. Order Matters
Place more specific/override classes last:
// ✅ Good - className can override defaults
cn("bg-blue-500 text-white", className)
// ❌ Bad - className is overridden by defaults
cn(className, "bg-blue-500 text-white")
2. Use for Tailwind Classes Only
Don’t mix with CSS modules or other class systems:
// ❌ Bad
cn("px-4", styles.customClass)
// ✅ Good
<div className={`${cn("px-4", className)} ${styles.customClass}`} />
3. Component Prop Pattern
Always accept className prop for flexibility:
interface CardProps {
className?: string;
children: React.ReactNode;
}
function Card({ className, children }: CardProps) {
return (
<div className={cn("default-classes", className)}>
{children}
</div>
);
}
Use Cases
- Building reusable component libraries
- Creating variant systems for components
- Handling conditional styling
- Allowing style overrides in components
- Managing complex Tailwind class combinations
- Preventing class conflicts
How It Works
-
clsx handles:
- Conditional classes
- Arrays of classes
- Object notation
{ "class": condition }
- Filtering falsy values
-
tailwind-merge handles:
- Tailwind class conflicts (e.g.,
px-2 vs px-4)
- Keeping only the last occurrence
- Understanding Tailwind’s class naming patterns
-
cn combines both:
- First processes conditionals with
clsx
- Then merges Tailwind classes with
twMerge
Dependencies
The cn function requires two packages:
{
"dependencies": {
"clsx": "^2.0.0",
"tailwind-merge": "^2.0.0"
}
}
These are automatically installed when using the Kokonut UI CLI.