5 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.
| 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.
| 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.
| 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.
| 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.
Lab evidence
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.