Plate 96
es-toolkit vs lodash-es: 2.5 KB vs 9.6 KB Gzip
Aditya Challa5 min read
I bundled the same six helpers on this box: es-toolkit 1.52.0 came in at 2,469 B gzip against lodash-es 4.18.1 at 9,632 B. On a 100-row nested object, es-toolkit's cloneDeep ran about 2.7× faster than lodash-es.
Short answer: pick es-toolkit for new code that uses a handful of lodash-style helpers and ships to the browser. Stay on lodash-es when you rely on lodash-only options such as debounce maxWait, or when a codemod would touch hundreds of files. No affiliate links in this post.
What I tested
- Machine: 8 vCPU Intel Xeon / 15 GB RAM shared Linux cloud box, tested 6 Oct 2026 about 01:04–01:06 IST.
- Versions: es-toolkit 1.52.0, lodash-es 4.18.1, Node 22.20.0, esbuild 0.28.2 (browser platform, ESM, minify) + gzip -9.
- Fixture: /workspace/bench-estoolkit-lodash/. The 6-helper entry imports debounce, groupBy, uniq, chunk, cloneDeep, and isEqual by name. Extra entries import debounce alone, and cloneDeep plus isEqual.
- Speed: 10,000-row arrays for groupBy, uniq, and chunk; a nested object with 100 rows for cloneDeep and isEqual. Median of 9 rounds after warm-up, run twice. Both libraries returned identical results on all four parity checks.
- Findings: 6-helper bundle 2,469 B vs 9,632 B gzip. debounce alone 337 B vs 1,449 B. cloneDeep 108 ms vs 291 ms per 2,000 clones; chunk 11.6 ms vs 26.3 ms per 2,000 calls.
- One surprise: es-toolkit is the smaller bundle but the bigger install. node_modules/es-toolkit was about 18 MB on disk against about 2.6 MB for lodash-es, because it ships CJS, ESM, compat, and type builds.
- Not tested: the es-toolkit/compat layer, lodash (CJS) with babel-plugin-lodash, throttle, get/set paths, TypeScript type checking speed, older browsers, or Rollup and webpack tree-shaking.
How big was each bundle?
| Entry (esbuild browser, minify) | Minified | gzip -9 |
|---|---|---|
| es-toolkit, 6 helpers | 6,826 B | 2,469 B |
| lodash-es, 6 helpers | 25,691 B | 9,632 B |
| es-toolkit, debounce only | 542 B | 337 B |
| lodash-es, debounce only | 2,914 B | 1,449 B |
es-toolkit was about 3.9× smaller gzip for the six helpers and about 4.3× smaller for debounce alone. Most of the lodash-es weight came from cloneDeep and isEqual: an entry with just those two was 6,622 B gzip in lodash-es against 1,939 B in es-toolkit. Put a ceiling on it with size-limit vs bundlesize. Minifier choice is a separate, smaller lever; I compared those in Terser vs esbuild vs SWC minify.
Which helpers were faster?
| Helper (median of 9) | Calls | es-toolkit | lodash-es | lodash ÷ es-toolkit |
|---|---|---|---|---|
| cloneDeep, 100-row object | 2,000 | 108.0 ms | 290.7 ms | 2.69× |
| chunk, 10k items by 100 | 2,000 | 11.6 ms | 26.3 ms | 2.27× |
| isEqual, 100-row object | 2,000 | 160.7 ms | 192.2 ms | 1.20× |
| uniq, 10k numbers | 500 | 94.3 ms | 99.1 ms | 1.05× |
| groupBy, 10k rows | 200 | 38.2 ms | 40.3 ms | 1.06× |
cloneDeep and chunk were clear wins, and the second run matched (2.72× and 2.24×). isEqual, uniq, and groupBy were close to a tie: on run two, isEqual was 1.06× and groupBy flipped to lodash-es at 0.97×. If you clone state often, compare this with structural sharing in Immer vs Mutative.
Which one should you pick?
| Situation | My pick |
|---|---|
| New browser app using 5–10 helpers | es-toolkit |
| Hot path that deep-clones objects | es-toolkit |
| You need debounce maxWait or lodash path strings | lodash-es |
| Large codebase with hundreds of lodash imports | lodash-es now, es-toolkit/compat later |
| Node-only script where bundle size does not matter | either |
Bottom line: es-toolkit 1.52.0 shipped 2.5 KB gzip against lodash-es 4.18.1 at 9.6 KB for six common helpers, and cloned a nested object about 2.7× faster. Who should not switch: teams whose code depends on lodash edge behavior. es-toolkit's debounce takes signal and edges options, not maxWait. Swap one helper at a time, check the build output with Rolldown vs Rollup, and remove lodash-es once knip vs depcheck reports it unused.
How this was made: I installed es-toolkit and lodash-es on the ShopperCove box, measured esbuild minify + gzip sizes for two entries, timed five helpers twice with parity checks, and saved the JSON (res-bundle.json, res-bundle-clone-eq.json, res-speed-run1.json, res-speed-run2.json). The write-up was drafted with AI help and checked against that output.
Sources
- https://github.com/toss/es-toolkit
- https://www.npmjs.com/package/es-toolkit
- https://es-toolkit.dev/
- https://github.com/lodash/lodash
- https://www.npmjs.com/package/lodash-es
Related
- https://www.shoppercove.com/blog/size-limit-vs-bundlesize
- https://www.shoppercove.com/blog/terser-vs-esbuild-swc-minify
- https://www.shoppercove.com/blog/immer-vs-mutative
- https://www.shoppercove.com/blog/rolldown-vs-rollup
- https://www.shoppercove.com/blog/knip-vs-depcheck
- https://www.shoppercove.com/blog/source-map-explorer-vs-webpack-bundle-analyzer
- https://www.shoppercove.com/blog/zod-vs-valibot
- https://www.shoppercove.com/blog/zustand-vs-redux-toolkit
Lab evidence
What I found running this
Hands-on on ShopperCove box 6 Oct 2026 ~01:04-01:06 IST (8 vCPU Intel Xeon / 15 GB shared Linux, Node 22.20.0). es-toolkit 1.52.0 vs lodash-es 4.18.1, esbuild 0.28.2 browser ESM minify + gzip -9. Fixture /workspace/bench-estoolkit-lodash/: named imports debounce, groupBy, uniq, chunk, cloneDeep, isEqual; extra debounce-only and cloneDeep+isEqual entries. Speed: 10k-row arrays (groupBy x200, uniq x500, chunk x2000), 100-row nested object (cloneDeep/isEqual x2000), median of 9 after warm, run twice. Run 1: gzip 2,469 vs 9,632 B; debounce 337 vs 1,449 B; cloneDeep 107.952 vs 290.740 ms; chunk 11.558 vs 26.259 ms; isEqual 160.737 vs 192.233 ms; uniq 94.268 vs 99.063 ms; groupBy 38.221 vs 40.339 ms (run 2 flipped to 0.97x). Parity 4/4. Disk ~18 MB vs ~2.6 MB. es-toolkit debounce has signal/edges, no maxWait; core has no get(). Raw: res-bundle.json, res-bundle-clone-eq.json, res-speed-run1.json, res-speed-run2.json. Not tested: es-toolkit/compat, babel-plugin-lodash, throttle, Rollup/webpack. No affiliate.
Related links
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.
5 Oct 2026
Plate 35
cookie-es vs cookie: 2.3 KB vs 1.4 KB Gzip
5 Oct 2026
Plate 81
scule vs change-case: 570 B vs 795 B Gzip
5 Oct 2026