A repository created on August 31 had reached 510 GitHub stars by September 2 for changing one small line in a common Tailwind CSS setup. The new cn package replaces both clsx and tailwind-merge behind the helper used across shadcn/ui components, while keeping the familiar cn(...) call sites. That makes the young project interesting for a reason bigger than its headline speed claim: developers can test a different merging engine without rewriting every component that depends on it. The MIT-licensed repository describes the package as a drop-in replacement with zero runtime dependencies.
The launch arrived into a large installed habit. In many Tailwind projects, clsx turns conditional strings, arrays, and objects into one class string, then tailwind-merge decides which conflicting utility wins. A component may pass px-2, a variant may add px-4, and the final output should retain the later padding class. According to the project README, cn preserves those input shapes and conflict rules while putting both jobs in one package.
The import is the easy part
Most shadcn/ui projects already hide the two libraries behind lib/utils.ts, so the manual migration is deliberately small. The old wrapper imports clsx, feeds its result into twMerge, and exports a local cn function. The replacement re-exports cn directly. The project also provides an automated migration through the shadcn CLI, as shown in its installation instructions.
npx shadcn@latest migrate cn
The package is broader than that command suggests. Its manifest provides ESM and CommonJS exports for the default API, a configurable entry, the merge engine, compiled tables, a compiler, and a lighter string-joining build. It declares Node 20 or newer, ships a command-line binary, and marks the package as side-effect free. The README says the runtime works with React, Vue, Svelte, Solid, Astro, plain server templates, browsers, Bun, Deno, and edge runtimes.
A migration can still leave the old code in a bundle. Libraries under node_modules may import clsx or tailwind-merge directly even after an application changes its own wrapper. The maintainers document aliases for Vite and Next.js that route those imports to cn. Teams should inspect their dependency graph before claiming a two-package removal, because changing the local helper alone does not control imports inside every dependency.
The 30x result belongs to one workload
tailwind-merge ships roughly 380 class groups as configuration and interprets them while the application runs. The maintainers say cn compiles the same rules ahead of time into flat tables. Its runtime walks input once, uses typed arrays for a character trie, compares integer identifiers for variants and conflicts, and slices surviving classes from the original string. That design is explained in the project's technical notes, alongside caches for repeated arguments, whole strings, and individual tokens.
The advertised 30x result comes from a component-shaped call with stable string identities. In the project's benchmark, clsx plus tailwind-merge took 320 nanoseconds and cn took 10 nanoseconds. Other rows tell a less uniform story: a warm typical string was 1.9 times faster, a cold render with many arbitrary values was three times faster, and the first call fell from 3.2 milliseconds to 0.4 milliseconds. The published benchmark table says each implementation and workload ran in an isolated process with its own warmup, with the best of five runs retained.
Those are maintainer-run microbenchmarks, so the multipliers should stay attached to their workloads. The documentation concedes that nanosecond results move by a few percent and that the cold cases are synthetic worst cases. It also reports a 37x geometric mean when replaying 144,265 calls collected from 58 open-source repositories. Outside replications would tell developers how those results move across processors, JavaScript engines, rendering patterns, and production bundles. The project's methodology page supplies enough detail to start that work.
Speed also carries a small transfer cost in the default mode. The maintainers measure cn at about 10.5 KB minified and compressed, versus roughly 8.6 KB for clsx plus tailwind-merge. Its minified code is slightly smaller to parse, 26.2 KB against 27.4 KB, because the compiled data is smaller than the old runtime configuration while the new engine contains more executable code. These figures in the size analysis make the trade clear: repeated calls get cheaper, while the default network payload grows by about 1.9 KB.
Compatibility carries more weight than nanoseconds
A class-merging library can be fast and still be unusable if one obscure modifier changes meaning. cn addresses that risk with differential testing against the libraries it replaces. The maintainers list 56,346 comparison cases, 300,000 grammar-fuzzed class strings, and 5,054 custom-configuration cases. CI also checks generated subsets, idempotence, and agreement between cached and uncached paths. The parity documentation is stronger evidence for a trial migration than a single nanosecond number, although the same project authors designed both the engine and the suite.
API coverage matters too. The default entry exports cn, twMerge, twJoin, and clsx; cn/config maps the extension helpers used by tailwind-merge. One experimental parser is absent, and Tailwind CSS 3 users are told to remain on tailwind-merge 2 because cn targets Tailwind 4 semantics. These limits are stated in the migration notes, so a codebase using lower-level exports needs an inventory before it runs the one-line command.
The safest evaluation is behavioral: pin the package version, run the application's visual and unit tests, then compare generated class strings around custom themes, arbitrary values, important modifiers, and prefixed utilities. The repository's own release script runs type checks, the conformance suite, and a size budget before publishing, according to its workspace manifest. An application with snapshot coverage can add its own evidence instead of relying on a benchmark that exercises someone else's components.
The optional build step has sharper edges
The default import needs no compilation. For applications that care about the extra 1.9 KB, cn build scans source files and emits tables containing only the class groups the project uses. Custom theme rules can be baked into those tables too. The build guide says generation takes a few hundred milliseconds and shows hooks for Next.js, Vite, and Turborepo; it also says the same inputs produce byte-identical output, which makes the generated file suitable for build caches.
Subsetting inherits the familiar limits of source scanning. A class assembled through string concatenation may be missed and needs a safelist. A newly introduced class group can pass through unmerged during a development session until the tables are regenerated. Production builds stay synchronized when the documented prebuild hook is present. Published component libraries should avoid this mode because their scan cannot see the classes used later by consumers, a warning the maintainers place directly in the setup documentation.
A fast patch cadence signals a young release
The npm registry listed cn 0.2.4 at reporting time, after five 0.2.x publications across September 1 and 2. The registry metadata also shows that this short package name existed years before the current release series, while the present manifest points to the shadcn-ui repository. The current package declares the MIT license and no runtime dependencies. Teams using lockfiles should still confirm the resolved version and integrity rather than assume every machine fetched the same fast-moving release.
The repository's commit history shows why version checks matter. Its first two days included fixes for published type declarations, an integer-wrap guard, content scanning, Unicode whitespace parity, and compatibility with extension definitions. Two more config-related changes were sitting on the main branch after version 0.2.4 was published. This is ordinary work for a new 0.x library, but it means a passing experiment against the repository tip may differ from the package currently recorded in a lockfile.
Watch the next npm release for those main-branch compatibility fixes, then look for benchmark reruns from users with large component trees. If the parity suite keeps absorbing real migration failures without widening the API gap, cn may earn a quiet place under thousands of components. For now, the project has made replacement unusually easy to test, and its own documented caveats give teams a concrete checklist for deciding whether the small bundle trade and young release are acceptable.