Components
cn (classNames) Helper

cn (classNames) Helper

The cn function is a utility helper designed to simplify the process of conditionally applying CSS classes in React components. It's particularly useful when working with Tailwind CSS, but can be used with any CSS framework or custom classes.

Introduction

The cn (classNames) helper function is a powerful utility designed to simplify the process of combining and conditionally applying CSS classes in React components. It is used in the creation of nearly all components and leverages two popular libraries, clsx and tailwind-merge, to provide an efficient and flexible way of handling class names, particularly when working with Tailwind CSS.

How it works

  1. clsx is used to conditionally construct className strings.
  2. tailwind-merge intelligently merges Tailwind CSS classes without style conflicts.
  3. The resulting function is exported as cn for use throughout the project.

Key features of the cn function:

  1. Accepts any number of arguments (strings, objects, or arrays) representing class names.
  2. Efficiently combines classes using clsx.
  3. Intelligently merges Tailwind classes to prevent conflicts using tailwind-merge.
  4. Automatically removes falsy values, duplicates, and excess whitespace.
  5. Supports conditional class application through object and array syntax.
import { cn } from "@/lib/utils"
 
function MyComponent({ variant, isActive, className }) {
  return (
    <div
      className={cn(
        "base-classes",
        variant === "primary" && "bg-blue-500 text-white",
        variant === "secondary" && "bg-gray-200 text-gray-800",
        isActive && "ring-2 ring-offset-2 ring-blue-500",
        className
      )}
    >
      {/* Component content */}
    </div>
  );
}
 
export default MyComponent;

Additional Examples

Combining with class-variance-authority

The cn function is often used in conjunction with class-variance-authority (CVA) to create flexible, variant-based components. This powerful combination allows for the creation of highly customizable components with a consistent API and styling structure.

How it works

  1. class-variance-authority is used to define a set of variant styles for a component.
  2. The cn function is then used to merge these variant styles with any additional classes, ensuring proper style hierarchy and avoiding conflicts.

Here's an example of how this is implemented in a Button component:

import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "@/lib/utils"
 
const buttonVariants = cva(
  "inline-flex items-center justify-center rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
  {
    variants: {
      variant: {
        default: "bg-primary text-primary-foreground hover:bg-primary/90",
        destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
        outline: "border border-input bg-background hover:bg-accent hover:text-accent-foreground",
        secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
        ghost: "hover:bg-accent hover:text-accent-foreground",
        link: "text-primary underline-offset-4 hover:underline",
      },
      size: {
        default: "h-10 px-4 py-2",
        sm: "h-9 rounded-md px-3",
        lg: "h-11 rounded-md px-8",
        icon: "h-10 w-10",
      },
    },
    defaultVariants: {
      variant: "default",
      size: "default",
    },
  }
)
 
interface ButtonProps
  extends React.ButtonHTMLAttributes<HTMLButtonElement>,
    VariantProps<typeof buttonVariants> {
  asChild?: boolean
}
 
import { Slot } from "@radix-ui/react-slot"
import { forwardRef } from "react"
 
const Button = forwardRef<HTMLButtonElement, ButtonProps>(
  ({ className, variant, size, asChild = false, ...props }, ref) => {
    const Comp = asChild ? Slot : "button"
    return (
      <Comp
        className={cn(buttonVariants({ variant, size, className }))}
        ref={ref}
        {...props}
      />
    )
  }
)
Button.displayName = "Button"
 
export { Button, buttonVariants }```