5 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.
| 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.
| 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.
| 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.
Lab evidence
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.
Hands-on6 Oct 20265 min read
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.
Hands-on5 Oct 20265 min read
Hands-on6 Oct 20266 min read