|
| 1 | +--- |
| 2 | +title: Benchmarks |
| 3 | +description: Detailed performance comparison of css-variants against class-variance-authority and tailwind-variants |
| 4 | +--- |
| 5 | + |
| 6 | +import { Card, CardGrid, Aside, Badge, Tabs, TabItem } from '@astrojs/starlight/components'; |
| 7 | + |
| 8 | +Performance matters. css-variants is engineered for speed, delivering exceptional runtime performance without sacrificing features. |
| 9 | + |
| 10 | +## Quick Summary |
| 11 | + |
| 12 | +| Comparison | Simple Ops | Compound Variants | Complex Components | Slots | |
| 13 | +|:-----------|:----------:|:-----------------:|:------------------:|:-----:| |
| 14 | +| **vs cva** | ~1.1-1.3x | **3-4x faster** | **2-7x faster** | — | |
| 15 | +| **vs tailwind-variants** | **3-5x faster** | **5-7x faster** | **10-11x faster** | **3-7x faster** | |
| 16 | + |
| 17 | +<Aside type="tip" title="Performance scales with complexity"> |
| 18 | +The more complex your components, the bigger the performance advantage. Real-world production components with multiple variants and compound rules see the most dramatic improvements. |
| 19 | +</Aside> |
| 20 | + |
| 21 | +--- |
| 22 | + |
| 23 | +## css-variants vs class-variance-authority |
| 24 | + |
| 25 | +Direct comparison against the popular [class-variance-authority](https://cva.style) (cva) library. |
| 26 | + |
| 27 | +### Basic Operations |
| 28 | + |
| 29 | +| Test Case | css-variants | cva | Difference | |
| 30 | +|-----------|-------------:|----:|:-----------| |
| 31 | +| Base class only | 23.2M ops/s | 21.2M ops/s | **1.09x faster** | |
| 32 | +| Single variant | 9.4M ops/s | 9.4M ops/s | Equal | |
| 33 | +| Multiple variants | 5.1M ops/s | 4.7M ops/s | **1.07x faster** | |
| 34 | +| Boolean variants | 5.9M ops/s | 5.0M ops/s | **1.18x faster** | |
| 35 | +| className override | 20.7M ops/s | 15.7M ops/s | **1.31x faster** | |
| 36 | + |
| 37 | +### Default Variants |
| 38 | + |
| 39 | +| Test Case | css-variants | cva | Difference | |
| 40 | +|-----------|-------------:|----:|:-----------| |
| 41 | +| Defaults (no props) | 9.3M ops/s | 8.0M ops/s | **1.16x faster** | |
| 42 | +| Override one default | 8.2M ops/s | 6.3M ops/s | **1.31x faster** | |
| 43 | +| With className | 8.4M ops/s | 6.6M ops/s | **1.27x faster** | |
| 44 | + |
| 45 | +### Compound Variants |
| 46 | + |
| 47 | +This is where css-variants really shines: |
| 48 | + |
| 49 | +| Test Case | css-variants | cva | Difference | |
| 50 | +|-----------|-------------:|----:|:-----------| |
| 51 | +| No match | 4.2M ops/s | 1.1M ops/s | **3.70x faster** | |
| 52 | +| Single match | 4.1M ops/s | 1.0M ops/s | **3.92x faster** | |
| 53 | +| Multiple matches | 3.8M ops/s | 0.9M ops/s | **4.24x faster** | |
| 54 | + |
| 55 | +<Aside type="tip" title="Why compound variants are faster"> |
| 56 | +css-variants uses optimized iteration and early-exit strategies when checking compound variant conditions, resulting in significantly better performance as complexity increases. |
| 57 | +</Aside> |
| 58 | + |
| 59 | +### Complex Real-World Components |
| 60 | + |
| 61 | +Testing a production-ready button component with 6 variant options and compound rules: |
| 62 | + |
| 63 | +| Test Case | css-variants | cva | Difference | |
| 64 | +|-----------|-------------:|----:|:-----------| |
| 65 | +| With defaults | 8.1M ops/s | 3.4M ops/s | **2.40x faster** | |
| 66 | +| With overrides | 5.1M ops/s | 0.7M ops/s | **6.93x faster** | |
| 67 | + |
| 68 | +--- |
| 69 | + |
| 70 | +## css-variants vs tailwind-variants |
| 71 | + |
| 72 | +Comparison against [tailwind-variants](https://www.tailwind-variants.org/), using its lightweight `/lite` build. |
| 73 | + |
| 74 | +### Basic Operations |
| 75 | + |
| 76 | +| Test Case | css-variants | tailwind-variants | Difference | |
| 77 | +|-----------|-------------:|------------------:|:-----------| |
| 78 | +| Base class only | 23.1M ops/s | 6.4M ops/s | **3.59x faster** | |
| 79 | +| Single variant | 9.9M ops/s | 2.2M ops/s | **4.52x faster** | |
| 80 | +| Multiple variants | 4.8M ops/s | 1.1M ops/s | **4.58x faster** | |
| 81 | +| Boolean variants | 5.8M ops/s | 1.6M ops/s | **3.57x faster** | |
| 82 | +| className override | 21.4M ops/s | 3.9M ops/s | **5.47x faster** | |
| 83 | + |
| 84 | +### Default Variants |
| 85 | + |
| 86 | +| Test Case | css-variants | tailwind-variants | Difference | |
| 87 | +|-----------|-------------:|------------------:|:-----------| |
| 88 | +| Defaults (no props) | 8.6M ops/s | 1.4M ops/s | **6.04x faster** | |
| 89 | +| Override one default | 8.6M ops/s | 1.4M ops/s | **6.35x faster** | |
| 90 | +| With className | 8.4M ops/s | 2.1M ops/s | **4.07x faster** | |
| 91 | + |
| 92 | +### Compound Variants |
| 93 | + |
| 94 | +| Test Case | css-variants | tailwind-variants | Difference | |
| 95 | +|-----------|-------------:|------------------:|:-----------| |
| 96 | +| No match | 4.2M ops/s | 0.6M ops/s | **6.60x faster** | |
| 97 | +| Single match | 4.0M ops/s | 0.7M ops/s | **5.43x faster** | |
| 98 | +| Multiple matches | 3.8M ops/s | 0.7M ops/s | **5.57x faster** | |
| 99 | + |
| 100 | +### Complex Real-World Components |
| 101 | + |
| 102 | +| Test Case | css-variants | tailwind-variants | Difference | |
| 103 | +|-----------|-------------:|------------------:|:-----------| |
| 104 | +| With defaults | 8.1M ops/s | 0.8M ops/s | **10.39x faster** | |
| 105 | +| With overrides | 5.2M ops/s | 0.5M ops/s | **11.05x faster** | |
| 106 | + |
| 107 | +--- |
| 108 | + |
| 109 | +## Slot-Based Components: scv vs tailwind-variants |
| 110 | + |
| 111 | +Comparing css-variants' `scv` (slot class variants) against tailwind-variants' slots feature. |
| 112 | + |
| 113 | +### Basic Slots |
| 114 | + |
| 115 | +| Test Case | css-variants (scv) | tailwind-variants | Difference | |
| 116 | +|-----------|-------------------:|------------------:|:-----------| |
| 117 | +| Base slots (2 slots) | 11.4M ops/s | 2.1M ops/s | **5.33x faster** | |
| 118 | +| Single variant (2 slots) | 3.9M ops/s | 1.2M ops/s | **3.25x faster** | |
| 119 | +| Multiple slots (4 slots) | 2.2M ops/s | 0.6M ops/s | **3.79x faster** | |
| 120 | +| Multiple variants (4 slots) | 1.8M ops/s | 0.4M ops/s | **4.67x faster** | |
| 121 | +| No variants (optimized) | 9.3M ops/s | 2.0M ops/s | **4.59x faster** | |
| 122 | + |
| 123 | +### Default Variants with Slots |
| 124 | + |
| 125 | +| Test Case | css-variants (scv) | tailwind-variants | Difference | |
| 126 | +|-----------|-------------------:|------------------:|:-----------| |
| 127 | +| Defaults (no props) | 2.8M ops/s | 0.7M ops/s | **3.95x faster** | |
| 128 | +| Override one default | 2.7M ops/s | 0.8M ops/s | **3.52x faster** | |
| 129 | +| With classNames | 2.8M ops/s | 1.0M ops/s | **2.75x faster** | |
| 130 | + |
| 131 | +### Compound Variants with Slots |
| 132 | + |
| 133 | +| Test Case | css-variants (scv) | tailwind-variants | Difference | |
| 134 | +|-----------|-------------------:|------------------:|:-----------| |
| 135 | +| No match | 1.8M ops/s | 0.3M ops/s | **6.04x faster** | |
| 136 | +| Single match | 1.7M ops/s | 0.2M ops/s | **7.45x faster** | |
| 137 | +| Multiple matches | 1.6M ops/s | 0.2M ops/s | **7.47x faster** | |
| 138 | + |
| 139 | +### Complex Slot Components |
| 140 | + |
| 141 | +| Test Case | css-variants (scv) | tailwind-variants | Difference | |
| 142 | +|-----------|-------------------:|------------------:|:-----------| |
| 143 | +| Card (7 slots, defaults) | 1.6M ops/s | 0.3M ops/s | **6.26x faster** | |
| 144 | +| Card (7 slots, overrides) | 0.8M ops/s | 0.1M ops/s | **7.32x faster** | |
| 145 | +| Stress test (8 slots) | 1.2M ops/s | 0.3M ops/s | **3.44x faster** | |
| 146 | + |
| 147 | +<Aside type="note" title="API Design Impact"> |
| 148 | +tailwind-variants returns slot functions that must be called individually, adding overhead per slot. css-variants returns pre-computed strings for all slots in a single call, making it fundamentally more efficient. |
| 149 | +</Aside> |
| 150 | + |
| 151 | +--- |
| 152 | + |
| 153 | +## Why css-variants is Faster |
| 154 | + |
| 155 | +### 1. Optimized Data Structures |
| 156 | + |
| 157 | +css-variants uses simple JavaScript objects and arrays with minimal indirection. No complex class instances or prototype chains. |
| 158 | + |
| 159 | +### 2. Early Exit Strategies |
| 160 | + |
| 161 | +When checking compound variants, css-variants exits as soon as a mismatch is found, avoiding unnecessary comparisons. |
| 162 | + |
| 163 | +### 3. Pre-computation |
| 164 | + |
| 165 | +Variant configurations are analyzed at creation time, not at runtime. The returned function is optimized for the specific configuration. |
| 166 | + |
| 167 | +### 4. Minimal String Operations |
| 168 | + |
| 169 | +Class strings are concatenated efficiently using a lightweight `cx` utility, avoiding expensive regex operations or repeated string allocations. |
| 170 | + |
| 171 | +### 5. No Runtime Dependencies |
| 172 | + |
| 173 | +Zero external dependencies means no additional function call overhead or module resolution costs. |
| 174 | + |
| 175 | +--- |
| 176 | + |
| 177 | +## Running Benchmarks Yourself |
| 178 | + |
| 179 | +Clone the repository and run the benchmarks on your machine: |
| 180 | + |
| 181 | +```bash |
| 182 | +git clone https://github.com/timphandev/css-variants.git |
| 183 | +cd css-variants |
| 184 | +yarn install |
| 185 | +cd internal/benchmark |
| 186 | +yarn start |
| 187 | +``` |
| 188 | + |
| 189 | +Results are saved to `internal/benchmark/dist/benchmark.json`. |
| 190 | + |
| 191 | +### Benchmark Configuration |
| 192 | + |
| 193 | +- **Framework**: Vitest Bench (using tinybench) |
| 194 | +- **Duration**: 500ms minimum per test |
| 195 | +- **Warmup**: Automatic by tinybench |
| 196 | +- **Environment**: Node.js |
| 197 | + |
| 198 | +--- |
| 199 | + |
| 200 | +## Methodology |
| 201 | + |
| 202 | +All benchmarks follow these principles: |
| 203 | + |
| 204 | +1. **Fair comparison**: Identical configurations for both libraries |
| 205 | +2. **Isolated setup**: Variant configs created outside bench blocks (not measured) |
| 206 | +3. **Real-world scenarios**: Test cases mirror actual production usage patterns |
| 207 | +4. **Statistical rigor**: Multiple iterations with standard deviation and percentile metrics |
| 208 | + |
| 209 | +<Aside type="tip" title="Benchmark Source Code"> |
| 210 | +View the complete benchmark suite on [GitHub](https://github.com/timphandev/css-variants/tree/main/internal/benchmark). |
| 211 | +</Aside> |
| 212 | + |
| 213 | +--- |
| 214 | + |
| 215 | +*Last updated: December 2025* |
0 commit comments