cnfast
Fast drop-in replacement for cn.
cnfast runs 3.8x faster on average than tailwind-merge, up to 7x on component-heavy code, with byte-identical output. Same API, no code changes.
import { cn } from "cnfast";
cn("px-2 py-1", isActive && "px-4", { "text-red-500": hasError });
// "py-1 px-4 text-red-500"
Install
npm install cnfast
Migrate an existing clsx, classnames, or tailwind-merge project in one command:
npx cnfast migrate
On a shadcn/ui project, add or replace your cn utility through the registry. This rewrites lib/utils.ts to re-export cnfast and installs the package:
npx shadcn@latest add aidenybai/cnfast/cn
Usage
Swap the shadcn/ui cn helper for cnfast:
// before
import { clsx, type ClassValue } from "clsx";
import { twMerge } from "tailwind-merge";
export const cn = (...inputs: ClassValue[]) => twMerge(clsx(inputs));
// after
export { cn } from "cnfast";
cnfast also exports clsx, twMerge, and twJoin.
Tagged templates
As a tagged template, cn caches by call-site identity, skipping the join and hash on every repeat. A stable call site runs 4.3x faster than tailwind-merge. The cn(...) call form already caches its arguments on V8, so on that engine the template form is only 1.2x ahead; the gap is wider on engines without that cache.
cn`px-2 px-4 ${isActive && "bg-blue-500"}`; // "px-4 bg-blue-500"
Comparing against cn
cnfast produces byte-identical output to tailwind-merge and computes it faster, with the largest gains on re-rendering call sites where the same class arguments recur:
Across the wider suite, operations per second on V8 (Node and Chrome), best-of-3:
| Workload | tailwind-merge | cnfast | Speedup |
|---|---|---|---|
| Cached re-render | 2,025 ops/s | 8,709 ops/s | 4.3x |
| Merge engine, cold | 1,440 ops/s | 5,411 ops/s | 3.8x |
| Component corpus | 1,585 ops/s | 6,506 ops/s | 4.1x |
| Page render | 4,249 ops/s | 11,908 ops/s | 2.8x |
| Live data grid | 500 ops/s | 2,185 ops/s | 4.4x |
Across 65 workloads the geometric mean is 3.8x, with 0 mismatches over 113,291 real-world call groups. The bundle is 9.43 KB gzipped against 8.45 KB for the baseline. Figures come from V8; see the benchmark suite for the Bun breakdown and the per-engine caveats.
cn runs once per element, so its cost scales with how much you render. Server-rendering a large page calls it across the whole tree; a client app that re-renders often (data grids, virtualized tables, live dashboards) calls it thousands of times per second, where a faster cn keeps frames within budget. On a small or rarely updated page, the saving stays within run-to-run noise.
Regenerate the chart with pnpm --filter cnfast bench:chart. See the architecture guide for how it works.
Development
pnpm install
pnpm build
pnpm test
Credits
cnfast adapts MIT-licensed code from clsx (Luke Edwards) and tailwind-merge (Dany Castillo). See LICENSE.
License
MIT
