cn()

The cn() function, popularized by shadcn/ui , is a utility function that helps manage conditional CSS classes in components.

Here is the function:

import { function clsx(...inputs: ClassValue[]): stringclsx, type type ClassValue = string | number | bigint | boolean | ClassArray | ClassDictionary | null | undefinedClassValue } from "clsx";
import { const twMerge: (...classLists: ClassNameValue[]) => stringtwMerge } from "tailwind-merge";

export function function cn(inputs: ClassValue[]): stringcn(inputs: ClassValue[]inputs: type ClassValue = string | number | bigint | boolean | ClassArray | ClassDictionary | null | undefinedClassValue[]) {
  return function twMerge(...classLists: ClassNameValue[]): stringtwMerge(function clsx(...inputs: ClassValue[]): stringclsx(inputs: ClassValue[]inputs));
}

This function combines two essential libraries that each serve a distinct purpose. The first is clsx , which handles conditional class name construction by intelligently joining classes based on conditions, while the second is tailwind-merge , which resolves styling conflicts by properly merging Tailwind CSS classes without letting them override each other incorrectly.

Consider the practical example below:

import { import cncn } from "@/lib/utils";
import * as React from "react";

interface ButtonProps extends React.type ComponentProps<T extends keyof React.JSX.IntrinsicElements | React.JSXElementConstructor<any>> = T extends React.JSXElementConstructor<infer Props> ? Props : T extends keyof React.JSX.IntrinsicElements ? React.JSX.IntrinsicElements[T] : {}
Used to retrieve the props a component accepts. Can either be passed a string, indicating a DOM element (e.g. 'div', 'span', etc.) or the type of a React component. It's usually better to use {@link ComponentPropsWithRef } or {@link ComponentPropsWithoutRef } instead of this type, as they let you be explicit about whether or not to include the `ref` prop.
@see{@link https://react-typescript-cheatsheet.netlify.app/docs/react-types/componentprops/ React TypeScript Cheatsheet}@example```tsx // Retrieves the props an 'input' element accepts type InputProps = React.ComponentProps<'input'>; ```@example```tsx const MyComponent = (props: { foo: number, bar: string }) => <div />; // Retrieves the props 'MyComponent' accepts type MyComponentProps = React.ComponentProps<typeof MyComponent>; ```
ComponentProps
<"button"> {
ButtonProps.className?: string | undefinedclassName?: string; } function function Button({ className, ...props }: ButtonProps): React.JSX.ElementButton({ className: string | undefinedclassName, ...
props: {
    ref?: React.Ref<HTMLButtonElement> | undefined;
    key?: React.Key | null | undefined;
    disabled?: boolean | undefined;
    form?: string | undefined;
    formAction?: string | ((formData: FormData) => void | Promise<void>) | React.DO_NOT_USE_OR_YOU_WILL_BE_FIRED_EXPERIMENTAL_FORM_ACTIONS[keyof React.DO_NOT_USE_OR_YOU_WILL_BE_FIRED_EXPERIMENTAL_FORM_ACTIONS] | undefined;
    ... 283 more ...;
    onTransitionStartCapture?: React.TransitionEventHandler<...> | undefined;
}
props
}: ButtonProps) {
return ( <JSX.IntrinsicElements.button: React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>button HTMLAttributes<HTMLButtonElement>.className?: string | undefinedclassName={import cncn("px-2 py-1 font-medium", className: string | undefinedclassName)} {...
props: {
    ref?: React.Ref<HTMLButtonElement> | undefined;
    key?: React.Key | null | undefined;
    disabled?: boolean | undefined;
    form?: string | undefined;
    formAction?: string | ((formData: FormData) => void | Promise<void>) | React.DO_NOT_USE_OR_YOU_WILL_BE_FIRED_EXPERIMENTAL_FORM_ACTIONS[keyof React.DO_NOT_USE_OR_YOU_WILL_BE_FIRED_EXPERIMENTAL_FORM_ACTIONS] | undefined;
    ... 283 more ...;
    onTransitionStartCapture?: React.TransitionEventHandler<...> | undefined;
}
props
} />
); }

This implementation serves two crucial purposes.

First, it allows you to easily pass conditional classes for dynamic styling. For example, you can write

<Button className={isPrimary && "bg-blue-500"}>Food</Button>

to conditionally apply a blue background only when needed, and clsx will handle the conditional logic cleanly.

Second, and more importantly, it automatically resolves styling conflicts through tailwind-merge. To understand why this matters, consider what happens without it. If your Button has default px-2 py-1 padding and you try to override it with <Button className="p-3">, the new p-3 class would be ignored due to CSS specificity rules(the original classes in the stylesheet would take precedence). However, with tailwind-merge working inside cn(), the function intelligently detects these conflicting utility classes and ensures that p-3 properly replaces the existing padding classes rather than being ignored.

cn is a simple utility function that is quite helpful when building flexible, reusable components with Tailwind CSS.

302 words

© 2023. All rights reserved.