Skip to content

tailwind-merge & clsx

When to Use

Use clsx alone for conditional class joining with no override conflicts. Use twMerge (via cn()) when a consumer needs to override component-internal classes.

Decision

Situation Use Why
Simple conditional classes, no overrides clsx alone Zero overhead, handles all conditional patterns
Merging classes where later values should override twMerge Understands p-2 p-4p-4, text-red-500 text-blue-500text-blue-500
Component that accepts a className prop cn() (clsx + twMerge) Allows consumers to override internal styles cleanly
High-frequency render path clsx alone if possible twMerge has overhead; profile before adding to hot paths

Pattern

// lib/utils.ts — standard cn() pattern
import { clsx, type ClassValue } from 'clsx';
import { twMerge } from 'tailwind-merge';

export function cn(...inputs: ClassValue[]) {
  return twMerge(clsx(inputs));
}

// Usage
cn('px-4 py-2', isLarge && 'px-6 py-3')    // handles conditions
cn('bg-red-500', props.className)            // consumer override wins
cn('p-2 p-4')                               // → 'p-4' (deduplicates)

tailwind-merge API (v3.5.0)

Function Purpose
twMerge(...inputs) Main function; pre-configured for Tailwind defaults
twJoin(...inputs) Like clsx but no conflict resolution; cheaper
extendTailwindMerge(config) Extend resolver for custom Tailwind utilities
createTailwindMerge(getConfig) Create a custom resolver from scratch

Extending for Custom Utilities

import { extendTailwindMerge } from 'tailwind-merge';

const twMerge = extendTailwindMerge({
  extend: {
    classGroups: {
      'font-size': [{ text: ['heading', 'label', 'caption'] }],
    },
  },
});

Version Compatibility

tailwind-merge version Tailwind CSS version
v3.x v4.x
v2.x v3.x

Not cross-compatible — upgrading Tailwind v3→v4 requires upgrading tailwind-merge v2→v3.

Common Mistakes

  • Wrong: Using twMerge everywhere without profiling. Right: clsx alone is sufficient when override conflicts aren't possible; twMerge runs a regex parser on every call.
  • Wrong: Expecting twMerge to handle arbitrary CSS-in-JS strings. Right: It only understands Tailwind class names.
  • Wrong: Using twJoin when you need conflict resolution. Right: twJoin is fast but doesn't deduplicate; use twMerge when px-2 px-4 must resolve to px-4.
  • Wrong: Forgetting to update tailwind-merge when upgrading Tailwind. Right: v2 doesn't know v4 class names and will fail to deduplicate them.

See Also