Plate 09
ohash vs object-hash: 10x Faster, 3.3 KB vs 11 KB
Aditya Challa5 min read
I hashed the parsed lockfile from a real hono checkout (1,070 packages) on this box: ohash 2.0.12 took about 3.5 ms per hash, while object-hash 3.0.0 took about 36 ms. That is roughly 10× faster, for 3.3 KB gzip instead of 11.4 KB.
Short answer: use ohash for new cache keys, memo keys and change detection. Keep object-hash only if you need its options (md5 or passthrough output, unorderedArrays), or if stored hashes would break when the format changes. 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:35 IST.
- Versions: ohash 2.0.12, object-hash 3.0.0 (last published Feb 2022), Node 22.20.0, esbuild 0.28.2 browser ESM minify + gzip -9.
- Inputs: a small nested app config (about 300 bytes as JSON) and the main document of hono's pnpm-lock.yaml parsed into an object (1,070 packages, 1,075 snapshots, about 313 KB as JSON). Scripts and results are in /workspace/bench-ohash-object-hash/.
- Method: 9 timed rounds after warm-up, median, two runs with the order reversed. object-hash ran with its default sha1 and with md5.
- Findings: lockfile object 3.59 / 3.45 ms (ohash) vs 36.3 / 35.8 ms (object-hash sha1). Small config 117,650 / 207,187 hashes per second vs 11,346 / 14,013.
- One surprise: picking md5 in object-hash did not help. Its time is spent walking and serializing the object, not in the digest.
- Not tested: browsers, Bun or Deno, circular references, typed arrays, very deep nesting, or hash collision quality.
Is ohash faster than object-hash?
| Input (median of 9, 2 runs) | ohash 2.0.12 | object-hash sha1 | object-hash md5 |
|---|---|---|---|
| Lockfile object, 1,070 packages | 3.59 / 3.45 ms | 36.3 / 35.8 ms | 37.4 / 36.4 ms |
| Small config, ops per second | 117,650 / 207,187 | 11,346 / 14,013 | 13,626 / 13,941 |
Yes, about 10× on the big object and 10–15× on the small one. The small-config number for ohash moved a lot between runs (JIT warm-up order), so read it as "an order of magnitude", not an exact figure. If you hash query keys on every render, compare this with how TanStack Query vs SWR build their keys.
How much smaller is ohash?
| Build (esbuild browser minify) | Minified | gzip -9 |
|---|---|---|
| ohash hash() | 6,867 B | 3,284 B |
| object-hash (browser build) | 35,284 B | 11,426 B |
About 3.5× smaller. object-hash's browser field points to a prebuilt file that brings its own crypto code, so tree-shaking cannot trim it. ohash ships a small JavaScript SHA-256 for browsers and uses node:crypto on Node; I checked that both paths gave the same digest for the same input. Track the difference in CI with size-limit vs bundlesize.
Do they treat edge cases the same?
| Case | ohash | object-hash |
|---|---|---|
| Same keys, different key order | equal | equal |
| Arrays [1,2] vs [2,1] | different | different (equal with unorderedArrays) |
| { b: undefined } vs key missing | different | different |
| Same Date instant | equal | equal |
| Date vs its ISO string | different | different |
| Map or Set in a different order | equal | equal |
| Class instance vs plain object | different | different |
| 1 vs "1" | different | different |
| Output for { a: 1 } | 43-char base64url SHA-256 | 40-char hex SHA-1 |
In 10 edge-case comparisons they agreed every time. What differs is the output: the strings look nothing alike, so switching libraries changes every stored hash. Plan one cache flush. For copying the objects you hash, see klona vs structuredClone.
Which one should you pick?
| Situation | My pick |
|---|---|
| New cache or memo keys | ohash |
| Browser bundle, size matters | ohash |
| Hashes already stored in a DB or CDN keys | stay on object-hash, or migrate with one flush |
| Need md5, passthrough or unorderedArrays | object-hash |
| Only need deep equality, no hash string | ohash isEqual |
Bottom line: ohash was about 10× faster and 3.5× smaller than object-hash on this box, with the same results on every edge case I tried. Who should not switch: systems that compare against hashes saved earlier and cannot rebuild them. If ohash replaces a lodash-style helper too, the es-toolkit vs lodash numbers show the same pattern of small modern packages. Content-hash caching across a monorepo is a different problem; see Turbo vs Nx.
How this was made: I installed both packages on the ShopperCove box, hashed a small config and a real parsed lockfile in two order-reversed runs, measured esbuild bundle sizes, and ran 10 edge-case comparisons plus a Node-vs-browser digest check. The JSON results are saved next to the scripts. The write-up was drafted with AI help and checked against that output.
Sources
- https://github.com/unjs/ohash
- https://github.com/puleos/object-hash
- https://www.npmjs.com/package/ohash
- https://www.npmjs.com/package/object-hash
Related
- https://www.shoppercove.com/blog/klona-vs-structuredclone
- https://www.shoppercove.com/blog/es-toolkit-vs-lodash
- https://www.shoppercove.com/blog/size-limit-vs-bundlesize
- https://www.shoppercove.com/blog/tanstack-query-vs-swr
- https://www.shoppercove.com/blog/turbo-vs-nx
- https://www.shoppercove.com/blog/immer-vs-mutative
- https://www.shoppercove.com/blog/knip-vs-depcheck
- https://www.shoppercove.com/blog/zod-vs-valibot
Lab evidence
What I found running this
Hands-on on ShopperCove box 6 Oct 2026 ~01:35 IST (8 vCPU Intel Xeon / 15 GB shared Linux, Node 22.20.0). ohash 2.0.12 vs object-hash 3.0.0, esbuild 0.28.2 browser ESM minify + gzip -9. Fixture /workspace/bench-ohash-object-hash/. Inputs: small nested config and hono pnpm-lock.yaml main document via js-yaml loadAll (1,070 packages, 1,075 snapshots, 313,361 chars JSON). Speed (9-round median, order-reversed): lock ohash 3.593 / 3.454 ms; object-hash sha1 36.307 / 35.821 ms; md5 37.358 / 36.353 ms. Small config ohash 117,650 / 207,187 ops/s; object-hash sha1 11,346 / 14,013; md5 13,626 / 13,941. Size: ohash 6,867/3,284 B vs object-hash browser dist 35,284/11,426 B. Behavior (res-behavior.json): key order ignored by both; array order, undefined-vs-missing, Date-vs-ISO, class-vs-plain, 1-vs-"1", different functions all differ in both; Map/Set order ignored by both; ohash JS SHA-256 equals node:crypto digest; object-hash unorderedArrays works. Not tested: browsers, Bun/Deno, circular refs, typed arrays, collision quality. No affiliate.
Related links
Plate 55
Klona vs structuredClone: 1.4M vs 225k Clones per Second
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 18
yaml vs js-yaml: 11x Faster Parse vs Kept Comments
5 Oct 2026