Plate 80
size-limit vs bundlesize: Should You Switch? 761ms vs 604ms
size-limit 11.2.0 (@size-limit/file) vs bundlesize 1.0.0-beta.2 on 3 prebuilt dist JS files: brotli median 761 ms vs gzip 604 ms; size-limit gzip mode 621 ms. App reported 57.69 kB brotli / 62.20 kB gzip / 60.74 kB bundlesize gzip.
Aditya Challa5 min read
On three prebuilt dist files (app 133 KB, vendor 87 KB, chunk 34 KB raw), size-limit 11.2.0 with @size-limit/file took a median 761 ms under its default brotli check, and bundlesize 1.0.0-beta.2 took 604 ms under gzip (~1.26x). When I forced size-limit to gzip, the median dropped to 621 ms, within noise of bundlesize.
Short answer: switch to size-limit if you want brotli budgets, webpack/esbuild plugins, or execution-time limits later. Stay on bundlesize if you only gate already-built gzip sizes and your CI already passes. Classic bundlesize 0.18.2 failed to install here (iltorb native build); the beta CLI is what actually ran. No affiliate links in this post.
What I tested
- Machine: 8 vCPU Intel Xeon / 15 GB RAM Linux cloud box, Node 20.19.2, tested 5 Oct 2026 (about 23:29 to 23:31 IST). Shared box; CLI runs interleaved after one warmup each.
- Fixture:
/workspace/bench-size-limit-bundlesize/dist/with three entropy-filled ESM bundles —app.js132,580 B,vendor.js87,380 B,chunk-home.js33,780 B. Same files for both tools. Limits set to 70 / 45 / 20 kB so both checks passed. - Tools:
size-limit11.2.0 +@size-limit/file11.2.0 (size-limit 14.x requires Node 22+ and crashed on this box with anode:fs/promisesglob import).bundlesize1.0.0-beta.2. Attemptedbundlesize@0.18.2; install failed oniltorb/ node-gyp. - Timing: seven interleaved
npx size-limitandnpx bundlesizewall clocks viaprocess.hrtimearound the full command. Separate five-run pass with"gzip": trueon each size-limit entry. - Findings: size-limit default (brotli) median 761.3 ms (runs 705 / 710 / 731 / 761 / 792 / 792 / 839). bundlesize gzip median 603.5 ms (587 / 590 / 603 / 604 / 604 / 604 / 618). size-limit gzip mode median 620.8 ms.
- Reported sizes for app: size-limit brotli 57.69 kB, size-limit gzip 62.20 kB, bundlesize gzip 60.74 kB. Same file, three different compressed numbers.
- One surprise: most of the 761 vs 604 gap was brotli work, not tool overhead. Gzip-mode size-limit landed within ~17 ms of bundlesize.
- Not tested:
@size-limit/webpackor@size-limit/esbuildbundling presets,@size-limit/timeexecution budgets, GitHub status checks, Windows/macOS, CI containers without localnpxcache.
Is size-limit slower than bundlesize?
| Variant | Median wall | Runs (ms) | Compression |
|---|---|---|---|
| size-limit 11.2 file (default) | 761 ms | 705 / 710 / 731 / 761 / 792 / 792 / 839 | brotli |
size-limit 11.2 file (gzip: true) | 621 ms | 562 / 595 / 621 / 634 / 638 | gzip |
| bundlesize 1.0.0-beta.2 | 604 ms | 587 / 590 / 603 / 604 / 604 / 604 / 618 | gzip |
On this fixture, yes for the defaults, no once compression matches. Both tools only read files that already exist under dist/; neither rebuilt the bundles. If your pipeline already emits gzip-equivalent artifacts and you only need a pass/fail byte gate, bundlesize is enough. If you want the same CLI to grow into webpack/esbuild entry budgets later, size-limit is the better home.
For minify and bundler choices that feed these files, see the terser vs esbuild/swc minify post and the rolldown vs rollup post.
Do the reported sizes match?
| File | Raw bytes | size-limit brotli | size-limit gzip | bundlesize gzip |
|---|---|---|---|---|
| dist/app.js | 132,580 | 57.69 kB | 62.20 kB | 60.74 kB |
| dist/vendor.js | 87,380 | 34.13 kB | 37.37 kB | 36.49 kB |
| dist/chunk-home.js | 33,780 | 12.48 kB | 13.70 kB | 13.38 kB |
No. Brotli beat gzip by about 4 to 5 kB on the app file, which is expected. Even in gzip mode the two CLIs disagreed by about 1.5 kB on app.js because they do not share one compressor implementation. Thresholds tuned on bundlesize numbers will look different under size-limit brotli, and slightly different under size-limit gzip. Re-baseline the day you switch.
Webpack-era apps that still emit a stats JSON may also want a visual analyzer; that is a different job from a CI byte gate. The Rspack vs webpack post and the webpack 5.111 ESM output post cover the bundler side.
Should you switch from bundlesize to size-limit?
| Situation | My pick |
|---|---|
CI only checks prebuilt dist/*.js gzip budgets | Either works; bundlesize was ~17 ms faster here in gzip mode |
| You want brotli budgets that match what CDNs serve | size-limit default |
| You plan to add webpack/esbuild entry measurement later | size-limit (file preset now, webpack/esbuild plugin later) |
| Stuck on classic bundlesize 0.18.x and install breaks on modern Node | Move to size-limit, or to bundlesize beta / bundlesize2 |
| Need GitHub check runs without writing your own action | Check each tool's current GitHub app docs before switching |
Bottom line: on three dist files, default size-limit was 761 ms and bundlesize was 604 ms; the gap mostly vanished when both used gzip. Who should not switch yet: teams whose release gate is wired to today's bundlesize gzip numbers and who cannot re-baseline this sprint. For a related unused-export cleanup gate, see the knip vs depcheck post.
How this was made: I generated the three dist fixtures, installed both CLIs on the ShopperCove test box, timed interleaved runs, and kept the JSON; the write-up was drafted with AI help and checked against that output.
Sources
- https://github.com/ai/size-limit
- https://www.npmjs.com/package/size-limit
- https://www.npmjs.com/package/@size-limit/file
- https://github.com/siddharthkp/bundlesize
- https://www.npmjs.com/package/bundlesize
- https://github.com/siddharthkp/bundlesize2
Related
- https://www.shoppercove.com/blog/terser-vs-esbuild-swc-minify
- https://www.shoppercove.com/blog/rolldown-vs-rollup
- https://www.shoppercove.com/blog/rspack-vs-webpack
- https://www.shoppercove.com/blog/webpack-5-111-esm-output-stable-september-2026
- https://www.shoppercove.com/blog/swc-vs-esbuild-babel
- https://www.shoppercove.com/blog/knip-vs-depcheck
- https://www.shoppercove.com/blog/vite-8-3-2-renderbuilturl-bundled-dev-sourcemaps-october-2026
- https://www.shoppercove.com/blog/lightningcss-vs-postcss
Lab evidence
What I found running this
Hands-on on ShopperCove box 5 Oct 2026 ~23:29-23:31 IST (8 vCPU Intel Xeon / 15 GB shared Linux, Node 20.19.2). Fixture: dist/app.js 132580 B, vendor.js 87380 B, chunk-home.js 33780 B (entropy-filled ESM). Tools: size-limit 11.2.0 + @size-limit/file 11.2.0; bundlesize 1.0.0-beta.2 (bundlesize@0.18.2 install failed on iltorb/node-gyp; size-limit 14.1.0 crashes on Node 20 via node:fs/promises glob). Limits 70/45/20 kB; both passed. 1 warmup each, then 7 interleaved npx wall clocks (process.hrtime). Medians: size-limit brotli 761.3 ms (705.1/710.2/731.3/761.3/792.2/792.4/838.8); bundlesize gzip 603.5 ms (587.2/589.5/602.8/603.5/603.6/604.2/618.0). size-limit gzip:true median 620.8 ms (562.4/594.9/620.8/634.4/638.4). Reported app size: brotli 57685 B, size-limit gzip 62197 B, bundlesize gzip 60.74KB. Raw: /workspace/bench-size-limit-bundlesize/res-size-limit-bundlesize.json. Not tested: webpack/esbuild/time presets, GitHub checks, Windows/macOS. No affiliate.
Related links
Plate 48
Zod vs Valibot: 1.14M vs 0.97M Parses
zod 4.6.5 vs valibot 1.5.0 on one nested user schema (uuid/email/age/tags/profile/enum). Warm medians after 2k warmup + 9×50k: zod.parse 1,144,494 ops/s vs valibot.parse 968,187 (~1.18×). safeParse fail: valibot 697,760 vs zod 415,523 (~1.7×). esbuild minify+gzip9: 452,999/92,636 B vs 85,126/15,422 B. Package trees ~6.1 MB vs ~1.9 MB. Fixture /workspace/bench-zod-valibot/.
5 Oct 2026
Plate 44
Playwright vs Cypress: 1.47s vs 4.24s Smoke
Playwright 1.55.1 Chromium vs Cypress 14.5.4 Electron on one local smoke (visit /, assert h1, click, assert). Warm medians (7 rounds after 1 warm-up): Playwright 1,466 ms vs Cypress 4,243 ms (~2.9x). Disk: PW node_modules ~12 MB + cache ~918 MB; Cypress node_modules ~23 MB + cache ~601 MB. Fixture /workspace/bench-pw-cy/.
5 Oct 2026
Plate 74
vite-plugin-checker: Plain Vite Shipped My TS Error
vite-plugin-checker 0.10.3 with enableBuild on Vite 7.3.6 / TS 5.9.3 / React 19: clean wall medians plain 1,949 ms vs checker 2,018 ms (+69 ms) vs tsc --noEmit 985 ms. Injected TS error: plain vite exit 0 (ships dist) x3; checker exit 2 x3; tsc exit 2 x3. Fixture 122 modules.
5 Oct 2026