Plate 55
Klona vs structuredClone: 1.4M vs 225k Clones per Second
Aditya Challa5 min read
I timed deep clones on this box: klona 2.0.6 copied a small app-state object about 1.4–1.5 million times per second, while the built-in structuredClone managed about 225,000. That is roughly 6.5× faster, for 477 B gzip of extra bundle that structuredClone does not cost you.
Short answer: use structuredClone by default, since it is free, handles circular references, and covers Date, Map, Set and typed arrays. Reach for klona in hot paths (reducers, undo stacks, per-request config copies) or when you need class instances to keep their prototype. 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:14 IST.
- Versions: klona 2.0.6 (default, klona/json, klona/lite, klona/full), Node 22.20.0 built-in structuredClone, JSON.parse(JSON.stringify()) as a baseline, esbuild 0.28.2 browser ESM minify + gzip -9.
- Fixture: /workspace/bench-klona-structuredclone/. Small payload: a 4-key app-state object with nested arrays and one Date, cloned 100,000 times. Large payload: 284 seo.json objects from my drafts folder (505 KB as JSON), cloned 50 times. 9 rounds, median, after warm-up. I ran it twice with the order reversed.
- Findings: small object, 1.42M and 1.50M ops/s for klona vs 227k and 224k for structuredClone. Large payload, 50 clones took 8.4 and 5.7 ms with klona vs 36.0 and 35.0 ms with structuredClone. All five methods I timed (klona/lite included) produced JSON-identical copies of the large payload. klona/lite landed between klona and klona/json (1.59M and 1.61M ops/s on the small object), so I left it out of the table below.
- One surprise: klona/json did not clone a Date, Map, Set, RegExp or typed array. It handed back the same object, so a later mutation would leak into the original. Only use it for plain JSON data.
- Not tested: browsers, Bun or Deno, Web Worker postMessage, very deep nesting, objects with getters or symbol keys, and memory use.
How much bundle does klona add?
| Build (esbuild browser minify) | Minified | gzip -9 |
|---|---|---|
| klona/json | 398 B | 268 B |
| klona/lite | 641 B | 378 B |
| klona (default) | 923 B | 477 B |
| klona/full | 1,059 B | 518 B |
| structuredClone (built in) | 54 B | 74 B |
The structuredClone row is just my one-line test entry; the function itself ships with the runtime. If you watch every byte, size-limit vs bundlesize will flag the 0.3–0.5 KB.
Is klona faster than structuredClone?
| Payload (median of 9) | klona | klona/json | structuredClone | JSON round-trip |
|---|---|---|---|---|
| small object, run 1 | 1.42M ops/s | 2.06M ops/s | 227k ops/s | 213k ops/s |
| small object, run 2 | 1.50M ops/s | 2.06M ops/s | 224k ops/s | 213k ops/s |
| 505 KB payload x 50, run 1 | 8.4 ms | 7.2 ms | 36.0 ms | 92.8 ms |
| 505 KB payload x 50, run 2 | 5.7 ms | 4.1 ms | 35.0 ms | 148.5 ms |
Yes, by a wide margin on this box. structuredClone pays for a serialize and deserialize step on every call. That cost barely matters for one clone per click, but it adds up in a store that copies state on every update. My Immer vs Mutative and Zustand vs Redux Toolkit posts cover the case where you can avoid deep copies altogether, and Jotai vs Zustand compares two small stores built on immutable updates.
What does each one lose or break?
| Value inside the object | klona | klona/json | structuredClone | JSON round-trip |
|---|---|---|---|---|
| Date, Map, Set, RegExp | cloned | same reference | cloned | lost |
| Typed array | cloned | same reference | cloned | lost |
| Class instance | prototype kept | lost | becomes plain object | lost |
| Function | same reference | same reference | throws DataCloneError | dropped |
| undefined value | kept | kept | kept | dropped |
| Circular reference | throws RangeError | throws RangeError | cloned | throws TypeError |
This table matters more than the speed one. structuredClone is the only option here that survived a circular object. klona is the only one that kept a class instance working, with its methods still callable after the copy.
Which one should you pick?
| Situation | My pick |
|---|---|
| Occasional copy of form data or API JSON | structuredClone |
| Data that may contain circular references | structuredClone |
| Store or undo stack cloning on every change | klona |
| Objects that must keep their class methods | klona |
| Hot path on plain JSON only | klona/json |
Bottom line: klona ran about 6.5× faster than structuredClone on small objects and about 4–6× faster on a 505 KB payload, for 477 B gzip. Who should not add it: apps that clone a few times per user action. The built-in is fast enough there, costs zero bytes, and handles cycles. If you are already trimming utility deps, compare notes with es-toolkit vs lodash.
How this was made: I installed klona on the ShopperCove box, measured esbuild minify+gzip sizes for each entry point, timed small and large clones in two order-reversed runs, ran a type-behavior check across Date, Map, Set, class, function and circular inputs, and saved the JSON results. The write-up was drafted with AI help and checked against that output.
Sources
- https://github.com/lukeed/klona
- https://www.npmjs.com/package/klona
- https://developer.mozilla.org/en-US/docs/Web/API/Window/structuredClone
- https://nodejs.org/api/globals.html
Related
- https://www.shoppercove.com/blog/size-limit-vs-bundlesize
- https://www.shoppercove.com/blog/immer-vs-mutative
- https://www.shoppercove.com/blog/zustand-vs-redux-toolkit
- https://www.shoppercove.com/blog/es-toolkit-vs-lodash
- https://www.shoppercove.com/blog/jotai-vs-zustand
- https://www.shoppercove.com/blog/zod-vs-valibot
- https://www.shoppercove.com/blog/tanstack-query-vs-swr
- https://www.shoppercove.com/blog/dayjs-vs-date-fns
Lab evidence
What I found running this
Hands-on on ShopperCove box 6 Oct 2026 ~01:14 IST (8 vCPU Intel Xeon / 15 GB shared Linux, Node 22.20.0). klona 2.0.6 (default, klona/json, klona/lite, klona/full) vs built-in structuredClone vs JSON.parse(JSON.stringify()), esbuild 0.28.2 browser ESM minify + gzip -9. Fixture /workspace/bench-klona-structuredclone/. Size gzip: klona 477 B, json 268, lite 378, full 518. Speed (9 rounds median, run twice with order reversed): small 4-key state object with a Date x 100,000 -> klona 1,423,933 / 1,502,413 ops/s, klona/json 2.06M, klona/lite 1.61M / 1.59M, structuredClone 227,278 / 224,234, JSON 212,842 / 212,607. 284 seo.json objects (505,296 B JSON) x 50 -> klona 8.418 / 5.691 ms, structuredClone 36.004 / 34.961 ms, JSON 92.779 / 148.538 ms. All copies JSON-identical. Behavior (res-behavior.json): structuredClone clones circular refs, drops class prototype, throws DataCloneError on functions; klona keeps class prototype, shares function refs, throws RangeError on circular; klona/json returns Date/Map/Set/RegExp/typed arrays by same reference. Not tested: browsers, Bun/Deno, postMessage, getters/symbols, memory. No affiliate.
Related links
Plate 91
Tinyexec vs execa: 2.7 KB vs 37 KB Gzip
5 Oct 2026
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 78
Clsx vs classnames: 294 B vs 857 B Gzip
5 Oct 2026