Skip to main content

Overview

The utilities module provides helper functions for managing CSS class names in React components, particularly when working with TailwindCSS and conditional styling.

cn

The cn utility function combines and merges CSS class names intelligently, handling conflicts and conditional classes with ease. It leverages clsx for flexible class name composition and tailwind-merge for proper TailwindCSS class merging.

Function Signature

function cn(...inputs: ClassValue[]): string

Parameters

inputs
ClassValue[]
required
A variable number of class name inputs. Each input can be:
  • A string of class names
  • An object with class names as keys and boolean values
  • An array of class names
  • undefined or null (automatically filtered out)
  • Any combination of the above

Returns

return
string
A merged string of CSS class names with TailwindCSS conflicts resolved. Later classes override earlier ones when conflicts occur.

Usage Examples

Basic Usage

import { cn } from '@/lib/utils';

// Simple class concatenation
const className = cn('text-red-500', 'font-bold');
// Result: "text-red-500 font-bold"

Conditional Classes

import { cn } from '@/lib/utils';

const isActive = true;
const hasError = false;

const className = cn(
  'base-class',
  isActive && 'active-class',
  hasError && 'error-class'
);
// Result: "base-class active-class"

TailwindCSS Conflict Resolution

import { cn } from '@/lib/utils';

// Later classes override earlier ones
const className = cn('text-red-500', 'text-blue-500');
// Result: "text-blue-500" (not "text-red-500 text-blue-500")

const responsiveClass = cn('p-4', 'md:p-8', 'lg:p-12');
// Result: "p-4 md:p-8 lg:p-12" (no conflicts, different breakpoints)

Object Syntax

import { cn } from '@/lib/utils';

const isPrimary = true;
const isDisabled = false;

const className = cn({
  'bg-blue-500': isPrimary,
  'bg-gray-500': !isPrimary,
  'opacity-50': isDisabled,
  'cursor-pointer': !isDisabled
});
// Result: "bg-blue-500 cursor-pointer"

React Component Example

import { cn } from '@/lib/utils';

interface ButtonProps {
  variant?: 'primary' | 'secondary';
  size?: 'sm' | 'md' | 'lg';
  className?: string;
}

const Button: React.FC<ButtonProps> = ({ 
  variant = 'primary', 
  size = 'md', 
  className 
}) => {
  return (
    <button
      className={cn(
        // Base styles
        'rounded font-semibold transition-colors',
        // Variant styles
        {
          'bg-blue-500 text-white hover:bg-blue-600': variant === 'primary',
          'bg-gray-200 text-gray-800 hover:bg-gray-300': variant === 'secondary',
        },
        // Size styles
        {
          'px-3 py-1 text-sm': size === 'sm',
          'px-4 py-2 text-base': size === 'md',
          'px-6 py-3 text-lg': size === 'lg',
        },
        // User-provided classes (can override defaults)
        className
      )}
    >
      Click me
    </button>
  );
};

// Usage
<Button variant="primary" size="lg" className="shadow-lg" />

Dependencies

Why Use cn?

Implementation Details

import { ClassValue, clsx } from "clsx";
import { twMerge } from "tailwind-merge";

export function cn(...inputs: ClassValue[]) {
  return twMerge(clsx(inputs));
}
The function works in two steps:
  1. clsx: Combines all inputs into a single class string, filtering out falsy values
  2. twMerge: Merges TailwindCSS classes, resolving conflicts by keeping the last occurrence

Build docs developers (and LLMs) love