The default import replaces two helpers with one package
Most Tailwind applications use clsx to join conditional values and tailwind-merge to remove conflicting utilities. cn combines those jobs behind familiar calls such as cn, twMerge, twJoin, and clsx. The package has 0 runtime dependencies and exports both ESM and CommonJS builds. It is framework-agnostic, so React is optional even though the migration command comes from the shadcn CLI.
The lowest-risk trial is a small wrapper change. An existing shadcn/ui project can re-export cn from its usual utility file, remove the old packages where nothing else imports them, and keep component call sites unchanged. The README also describes aliases for dependencies that still import clsx or tailwind-merge. Those aliases have limits: a default clsx import will not resolve through the documented mapping, and a bundled copy inside another library cannot be intercepted.
The speed claim depends on compiled rules and repeated calls
The package compiles Tailwind conflict rules into flat lookup tables instead of interpreting a configuration object during each call. Its engine uses typed arrays, integer comparisons, and caches for repeated argument sequences, whole strings, and tokens. The README reports a 30-fold result for its representative component call and a 37-fold geometric mean across 58 open-source repositories. Those are the project's benchmarks, not results from our sandbox.
Its methodology is better documented than a bare chart. Each implementation and workload runs in a separate process with its own warmup, and the harness keeps the best of 5 timed blocks. The internals guide also admits that nanosecond results vary and that cold workloads are synthetic. It says the default entry is about 10.5 KB after minification and gzip, roughly 1.9 KB larger than clsx plus tailwind-merge. Faster calls do not automatically mean a smaller default download.
What happened when we ran it
Our sandbox installed 185 pnpm packages in 9 seconds and occupied 108 MB. The checked-out commit was ee3ac80, containing 123 files, about 9,684 source lines, and 10.3 MB before dependencies. The build completed in 6 seconds. The available test command then completed successfully in 17 seconds.
Nothing failed in those repository checks. The scan found 3 CI workflow files and a pnpm workspace, with no Dockerfile and no top-level tests directory. We did not run the project's performance benchmarks, replay 58 external corpora, or measure a production bundle. Our result establishes that install, build, and test worked in a fresh unprivileged Debian container with 3 CPUs, 8 GB of RAM, Node 22, and no secrets.
Project-fitted tables save bytes but impose source-scanning rules
The normal import ships rules for the full supported Tailwind set. cn build takes another route: it scans an application's files and writes a table containing the conflict groups that appear there. Vite and Next.js adapters watch sources during development and regenerate for production. The guide recommends the default table for most applications and reserves compilation for a smaller bundle or custom theme work.
Static discovery creates the same boundary Tailwind users already know. A class assembled as "p-" + size is invisible to the scanner unless you add it to a safelist. With npm lifecycle hooks, a newly introduced group may pass through without merging until the development server restarts. The framework plugins watch and close that gap. Published component libraries should not ship fitted tables because a consumer can use valid classes the library's own source never contained.
Two open reports weaken the full-parity promise
The README says output matches tailwind-merge, and the repository has a substantial differential and fuzzing setup. Open issue 142 still gives examples where legacy bg-gradient-to-* utilities are assigned to a different conflict group. Open issue 23 reports that non-numeric arbitrary font-[...] values can be treated as font weight rather than font family. Both reports include concrete inputs and observed outputs.
That does not make every migration unsafe. It means you should test the class patterns your design system actually uses, especially gradients, arbitrary values, custom themes, and aliases. The public API table also names one unsupported export, experimentalParseClassName. A migration tool can change imports, but it cannot decide whether a rare semantic difference alters a visible component. Snapshotting merged outputs or key rendered states is cheap insurance.
Tailwind 4 and Node 20 define the supported lane
cn targets Tailwind CSS 4 semantics. The README tells Tailwind 3 projects to stay on tailwind-merge 2. The CLI requires Node 20 or newer, while the runtime works in browsers, Node, Bun, Deno, and edge environments. Custom configuration supports extension, override, prefixes, validators, and theme-derived rules through a separate entry point.
Version 0.4.0 was released on September 22, 2026, the same date as the last push we fetched. GitHub showed 1,600 stars and 5 combined open issues and pull requests. Three were issues and 2 were pull requests in the current listing, including the parity reports above. The MIT license is clear, and 3 CI workflows cover benchmarks, checks, and releases. Recent maintenance is visible despite the young version number.
Output checks matter more than the speed headline
A Tailwind 4 application can try the default import with little ceremony: our install, build, and test sequence took 32 seconds in total. Keep the original helper available on a branch, compare merged output for representative components, and inspect arbitrary classes before removing it. If that passes, the API compatibility and zero runtime dependencies make cn a reasonable replacement.
Stay with tailwind-merge when you need Tailwind 3 support or exact incumbent behavior for the reported edge cases. Use clsx alone when conflict resolution is unnecessary. The optional compiler deserves a later decision, after the plain package proves correct in your codebase. That order captures the simple migration without making source scanning part of day one.

