Lesson 23 / 25

Conditional Classes and Conflicts

clsx and tailwind-merge.

Which class wins?

When two utilities set the same property (p-2 and p-4), the winner depends on their order in the generated stylesheet, not their order in the class attribute, so passing an override through className may not work. Libraries help: clsx builds class strings conditionally, and tailwind-merge resolves conflicts so the last conflicting class wins. Many component libraries combine them in a cn() helper.

A cn() helper

Combining clsx and tailwind-merge (community libraries).

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

export function cn(...inputs) {
  return twMerge(clsx(inputs));
}

// cn("px-4 py-2 bg-cyan-600", isActive && "bg-cyan-800", "px-6")
// -> padding px-6 and bg-cyan-800 win when isActive is true

Design props instead of raw overrides

A size="lg" prop is clearer and safer than letting callers pass arbitrary conflicting classes.

Quick check: What decides which of two conflicting utilities wins without tooling?

  • Their order in the generated CSS
  • Their order in the class attribute
  • Alphabetical order
  • The longer class name
Answer

Their order in the generated CSS — Use tailwind-merge for predictable overrides.