Use clsx to decide which class names belong on an element, then pass the result to tailwind-merge when later classes should override conflicting Tailwind utilities. Together, they make conditional styles readable and component className overrides predictable.
What Tailwind CSS does—and does not do
Tailwind is a utility-first styling system: you style elements by combining single-purpose presentation classes in markup, as the Tailwind documentation explains. Variants apply utilities conditionally to states, themes, or viewport sizes; examples include hover:, focus:, dark:, sm:, md:, and lg:. The default sm breakpoint is 40rem (640px), according to Tailwind’s responsive design documentation.
Tailwind does not assemble conditional class strings for your components, and the order of class names in HTML is not a dependable way to resolve competing utilities. When two utilities target the same CSS property, the one later in the generated stylesheet wins; stylesheet order is not necessarily the order in your class attribute. Tailwind describes this behavior in its guidance on conflicting utility classes.
What clsx and tailwind-merge each do
clsx includes classes conditionally
clsx constructs a class string from strings, arrays, objects, and boolean conditions. It includes the values that apply and drops falsey values. Its README describes it as a small utility for constructing class names conditionally. It does not understand Tailwind’s utility groups, so it will include both px-2 and px-4 if both are passed.
#1 Best Overall
tailwind-merge resolves known Tailwind conflicts
twMerge, from tailwind-merge, understands Tailwind utility conflicts and removes earlier conflicting classes so the later one takes effect. For example, twMerge("px-2", "px-4") returns px-4. The API reference says its default configuration suits Tailwind’s default configuration or a close equivalent; custom class groups or themes may require extendTailwindMerge.
Combine them in a reusable component helper
A common helper, often named cn, runs conditional assembly first and conflict resolution second:
Rank #2
import { clsx, type ClassValue } from "clsx";
import { twMerge } from "tailwind-merge";
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}
For a component, put defaults before the caller’s className so the caller’s conflicting utilities are later inputs to the merge:
function Button({ className, disabled, intent = "primary", ...props }) {
return (
<button
className={cn(
"inline-flex items-center rounded-md px-4 py-2",
disabled && "cursor-not-allowed opacity-50",
{ "bg-blue-600 text-white": intent === "primary" },
className,
)}
disabled={disabled}
{...props}
/>
);
}
A consumer can now pass, for example, px-6 to replace the default horizontal padding, while unrelated classes remain. clsx determines which tokens are present; twMerge resolves conflicts it recognizes. This is especially useful at reusable component boundaries, where a caller needs to adjust a component’s defaults.
Free tools Windows power users keep installed
One-click scans. No signup required.
Keep dynamically selected Tailwind classes detectable
Tailwind generates CSS by scanning project files for class-like tokens. If code constructs a class from fragments—such as `bg-${color}-500`—the complete class names may not appear in source for the scanner to detect. Tailwind documents this scanning behavior in its source detection documentation.
Prefer mapping each choice to a complete class string in source:
Rank #4
const backgrounds = {
blue: "bg-blue-500",
red: "bg-red-500",
};
const className = backgrounds[color] ?? backgrounds.blue;
Use your project’s explicit safelist or equivalent configuration when complete candidates cannot be kept in scanned source. clsx and tailwind-merge operate on the strings at runtime; neither makes a missing CSS utility appear in the generated stylesheet.
Quick Recap
Best Value
Choose the right helper for the job
| Need | Use | What it does |
|---|---|---|
| Conditional classes with strings, objects, or arrays | clsx |
Includes applicable values; does not resolve Tailwind conflicts. |
| Joining classes when all supplied tokens should remain | twJoin |
Joins without conflict resolution; the tailwind-merge API reference describes it as a direct subset of clsx. |
| Conditional classes plus Tailwind conflict resolution | twMerge(clsx(...)), often wrapped as cn |
Assembles the classes, then resolves recognized conflicts. |
| Custom Tailwind utilities or theme groups | extendTailwindMerge |
Extends tailwind-merge’s configuration to recognize custom conflict groups; consult the API reference. |
Common mistakes to avoid
- Expecting clsx to resolve conflicts: it will include both classes; use
twMergewhen they are conflicting Tailwind utilities. - Assuming the last class in the HTML string always wins: CSS stylesheet order determines which declaration wins, so use a Tailwind-aware merge at the component boundary instead.
- Building class names from invisible fragments: keep complete candidate utilities in scanned source or configure an explicit safelist.
- Using the default merge configuration with unrecognized custom utilities: extend the conflict groups when your Tailwind setup adds custom utilities or theme groups.
- Adding conflict resolution when simple joining is enough: use
twJoinwhen no conflicting utilities need to be removed.
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.
Quick wins for a faster PC:
Repair Windows errors before they cause bigger problemsFix Now →Scan for outdated or missing drivers - takes under a minuteDriver Scan →

