Skip to main content

Guide

ohash vs object-hash vs hash-wasm 2026

Compare ohash, object-hash, and hash-wasm for hashing objects and data in JavaScript. Deterministic hashing, SHA-256, content addressing, cache keys, and.

·PkgPulse Team·
0

TL;DR

ohash is the UnJS super-fast object hashing library — deterministic hash of any JavaScript value, tiny, zero dependencies, used for cache keys and content addressing. object-hash is the mature object hashing library — SHA-1/SHA-256/MD5 of JavaScript objects, handles circular references, configurable key ordering. hash-wasm is a WebAssembly-based hashing library — blazing fast SHA-256, MD5, xxHash, Blake3 on raw data (strings/buffers), not specific to objects. In 2026: ohash for hashing JS objects (cache keys, ETags), object-hash for configurable object hashing, hash-wasm for raw data hashing at maximum speed.

Key Takeaways

  • ohash: ~10M weekly downloads — UnJS, fast object hashing, deterministic, tiny
  • object-hash: ~10M weekly downloads — configurable, multiple algorithms, circular reference support
  • hash-wasm: ~3M weekly downloads — WASM-based, 30+ algorithms, raw data hashing
  • ohash produces a quick hash for cache invalidation — not cryptographic
  • object-hash produces SHA-1/SHA-256 — deterministic and verifiable
  • hash-wasm is for raw data — hashing strings, buffers, files (not JS objects)

Common Use Cases

Why hash JavaScript objects?
  ✅ Cache keys — hash(requestParams) → unique cache key
  ✅ ETags — hash(responseBody) → HTTP ETag header
  ✅ Change detection — hash(config) changed? → reload
  ✅ Deduplication — hash(record) → check if already processed
  ✅ Content addressing — hash(data) → deterministic filename

Why hash raw data?
  ✅ File integrity — SHA-256(file) → verify downloads
  ✅ Password hashing — (use bcrypt/argon2 instead)
  ✅ Checksums — MD5(payload) → quick integrity check
  ✅ Bloom filters — xxHash(key) → fast probabilistic lookup

ohash

ohash — fast object hashing:

Basic usage

import { hash, objectHash, murmurHash, sha256 } from "ohash"

// hash() — quick hash of any value:
hash({ name: "react", version: "19.0.0" })
// → "aBcDeFgH" (short, deterministic hash string)

hash([1, 2, 3])
// → "xYzAbCdE"

hash("hello world")
// → "qRsTuVwX"

// Same input → same output (deterministic):
hash({ a: 1, b: 2 }) === hash({ b: 2, a: 1 })
// → true (key order doesn't matter)

Object hashing for cache keys

import { hash } from "ohash"

// API response caching:
function getCacheKey(endpoint: string, params: Record<string, unknown>) {
  return hash({ endpoint, params })
}

const key = getCacheKey("/api/packages", { sort: "downloads", limit: 10 })
// → deterministic cache key

// ETag generation:
function generateETag(data: unknown) {
  return `"${hash(data)}"`
}

app.get("/api/packages", (req, res) => {
  const packages = getPackages()
  const etag = generateETag(packages)

  if (req.headers["if-none-match"] === etag) {
    return res.status(304).end()  // Not modified
  }

  res.setHeader("ETag", etag)
  res.json(packages)
})

SHA-256 and Murmur hash

import { sha256, murmurHash } from "ohash"

// SHA-256 (cryptographic):
sha256("hello world")
// → "b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9"

// Murmur hash (fast, non-cryptographic):
murmurHash("hello world")
// → 1586663183

// sha256 for content integrity:
const contentHash = sha256(JSON.stringify(config))

Diff and comparison

import { hash, diff } from "ohash"

// diff() — find differences between objects:
const changes = diff(
  { name: "react", version: "18.0.0", downloads: 5_000_000 },
  { name: "react", version: "19.0.0", downloads: 5_500_000 }
)
// → [
//   { type: "changed", key: "version", oldValue: "18.0.0", newValue: "19.0.0" },
//   { type: "changed", key: "downloads", oldValue: 5000000, newValue: 5500000 }
// ]

// isEqual — deep equality check:
import { isEqual } from "ohash"
isEqual({ a: 1 }, { a: 1 }) // → true

object-hash

object-hash — configurable object hashing:

Basic usage

import objectHash from "object-hash"

// SHA-1 by default:
objectHash({ name: "react", version: "19.0.0" })
// → "a1b2c3d4e5f6..." (40-char SHA-1 hex)

// SHA-256:
objectHash({ name: "react" }, { algorithm: "sha256" })
// → "abcdef123456..." (64-char SHA-256 hex)

// MD5:
objectHash({ name: "react" }, { algorithm: "md5" })
// → "a1b2c3d4..." (32-char MD5 hex)

Configuration options

import objectHash from "object-hash"

// Key ordering (default: sorted):
objectHash({ b: 2, a: 1 }) === objectHash({ a: 1, b: 2 })
// → true (keys sorted before hashing)

// Disable key sorting:
objectHash({ b: 2, a: 1 }, { unorderedObjects: false })
// → different from objectHash({ a: 1, b: 2 }, { unorderedObjects: false })

// Exclude specific keys:
objectHash({ name: "react", _internal: "skip" }, {
  excludeKeys: (key) => key.startsWith("_"),
})

// Respect type differences:
objectHash({ value: 1 }, { respectType: true })
// Different from objectHash({ value: "1" })

Circular references

import objectHash from "object-hash"

// Handles circular references:
const obj: any = { name: "react" }
obj.self = obj  // Circular!

objectHash(obj)
// → works fine, circular reference detected and handled

// ohash would throw on circular references by default

Specific value hashing

import objectHash from "object-hash"

// Hash specific types:
objectHash.sha1({ name: "react" })     // SHA-1
objectHash.MD5({ name: "react" })      // MD5
objectHash.keysMD5({ name: "react" })  // MD5 of keys only

// Write to stream (for large objects):
objectHash.writeToStream({ name: "react" }, { algorithm: "sha256" })

hash-wasm

hash-wasm — WASM-based hashing:

Fast hashing

import { sha256, md5, xxhash64, blake3 } from "hash-wasm"

// SHA-256:
const hash = await sha256("hello world")
// → "b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9"

// MD5:
const md5Hash = await md5("hello world")
// → "5eb63bbbe01eeed093cb22bb8f5acdc3"

// xxHash64 (ultra-fast, non-cryptographic):
const xxHash = await xxhash64("hello world")
// → "d4a1185c118f2a57"

// Blake3 (modern, fast cryptographic):
const blake = await blake3("hello world")
// → "d74981efa70a0c880b8d8c1985d075dbcbf679b99a5f9914e5aaf96b831a9e24"

Buffer/file hashing

import { sha256, createSHA256 } from "hash-wasm"
import { readFile } from "node:fs/promises"

// Hash a file:
const buffer = await readFile("package.json")
const fileHash = await sha256(buffer)

// Streaming hash (for large files):
const hasher = await createSHA256()
hasher.init()

// Process in chunks:
for await (const chunk of readStream) {
  hasher.update(chunk)
}

const result = hasher.digest("hex")

Performance advantage

Benchmark (hashing 1 MB of data):

  hash-wasm (SHA-256, WASM):  ~450 MB/s
  Node.js crypto (SHA-256):    ~400 MB/s
  pure JS sha256:              ~50 MB/s

  hash-wasm (xxHash64, WASM): ~3,000 MB/s
  Node.js crypto (N/A):       —
  pure JS xxhash:             ~200 MB/s

hash-wasm is fastest in browsers (no native crypto).
In Node.js, built-in crypto is comparable for SHA-256.
xxHash/Blake3 via hash-wasm is much faster than any pure JS option.

Available algorithms

import {
  md5, sha1, sha256, sha512,       // Standard
  xxhash32, xxhash64, xxhash128,   // xxHash (non-crypto, ultra-fast)
  blake2b, blake2s, blake3,         // Blake (modern crypto)
  crc32, adler32,                   // Checksums
  argon2id, bcrypt, scrypt,         // Password hashing
  sha3_256, sha3_512,               // SHA-3
  keccak256, keccak512,             // Keccak
} from "hash-wasm"

// 30+ algorithms available — all WASM-accelerated

Feature Comparison

Featureohashobject-hashhash-wasm
Hash JS objects❌ (raw data)
Hash raw data✅ (sha256)
Deterministic
Key order independent✅ (configurable)N/A
Circular referencesN/A
Algorithm choicemurmur, SHA-256SHA-1, SHA-256, MD530+ algorithms
WASM accelerated
Streaming
Object diff
Browser support
Dependencies000
Weekly downloads~10M~10M~3M

When to Use Each

Use ohash if:

  • Need fast cache keys from JavaScript objects
  • Building ETags or content-addressed storage
  • In the UnJS ecosystem (Nuxt, Nitro, H3)
  • Want object diff and equality utilities alongside hashing

Use object-hash if:

  • Need configurable hashing (algorithm, key ordering, exclusions)
  • Objects may have circular references
  • Want SHA-1/SHA-256/MD5 hashes of JavaScript objects
  • Need streaming hash support for large objects

Use hash-wasm if:

  • Hashing raw data (strings, buffers, files) not JS objects
  • Need maximum performance (WASM-accelerated)
  • Want exotic algorithms (xxHash, Blake3, Keccak)
  • Building file integrity checks, checksums, or content addressing

Handling Undefined, Functions, and Special JavaScript Values

A practical consideration when hashing JavaScript objects is how each library handles values that don't serialize cleanly to JSON. undefined, functions, Symbol, circular references, Date, Map, Set, RegExp, and class instances all have subtly different behavior across the three libraries, and incorrect assumptions lead to bugs where two different objects hash to the same value (or fail with an error).

ohash serializes objects by converting them to a canonical string representation. undefined values are omitted from serialized object properties (consistent with JSON.stringify), so { a: 1, b: undefined } hashes identically to { a: 1 }. Functions are serialized using their source code text, meaning two functions with identical implementations but different names produce different hashes. Dates are serialized as their ISO string representation. ohash does not handle circular references by default and will enter an infinite loop — use serializeCircular: true option to enable safe circular reference handling.

object-hash is the most comprehensive in handling edge cases. It serializes Date as the numeric timestamp, RegExp as its source and flags, Function as its source code, Set and Map as sorted arrays of their entries, and circular references by tracking previously-seen objects and inserting a back-reference marker. This makes object-hash the safest choice when hashing arbitrary runtime objects whose structure you don't fully control — configuration objects that may contain non-JSON-serializable values, API response objects that may include unexpected types, or third-party library objects.

hash-wasm operates on raw bytes and cannot hash JavaScript objects directly — you must serialize them first (typically with JSON.stringify) and pass the resulting string or buffer. This means you must handle special values yourself before hashing. For the raw-data hashing use cases that hash-wasm targets (file buffers, binary protocol data), this is not a limitation since the input is already a Buffer or Uint8Array.

Migration Guide

From object-hash to ohash for cache keys

The most common use case is generating stable cache keys from JavaScript objects. ohash is faster and zero-dependency, making it the better default for this pattern:

// object-hash (old)
import objectHash from "object-hash"
const cacheKey = objectHash({ userId: "123", filters: { status: "active", page: 1 } })
// → "a3f9b0..." (SHA-1 by default)

// ohash (new)
import { hash } from "ohash"
const cacheKey = hash({ userId: "123", filters: { status: "active", page: 1 } })
// → "v38sm2..." (murmur hash, faster, deterministic)

// ohash's hash is designed for cache keys, not security:
// - Don't use ohash for password hashing or content verification signatures
// - Use hash-wasm or Node.js crypto for security-sensitive hashing

ohash handles key ordering, circular reference detection for common cases, and type coercion consistently. The output format differs from object-hash but is equally deterministic.

Community Adoption in 2026

ohash and object-hash each reach approximately 10 million weekly downloads — a statistical coincidence that reflects very different adoption stories. ohash's downloads are dominated by transitive dependencies through the UnJS ecosystem (Nitro, H3, Nuxt's caching internals). Direct intentional use of ohash is lower; most consumers have it installed because Nuxt or a Nuxt-compatible tool pulled it in. object-hash's downloads represent direct use in caching middleware, ETagger libraries, and content-addressed storage implementations where teams explicitly chose it for its configurable algorithm support.

hash-wasm reaches approximately 3 million weekly downloads, used primarily in browser-based applications where native crypto access is limited and pure-JavaScript hashing is too slow. In Node.js environments, hash-wasm competes directly with the built-in crypto module — and loses on convenience while winning on breadth of algorithms (xxHash64, Blake3, Keccak are not available in Node.js crypto). For teams that need non-standard hash algorithms, hash-wasm is the most complete option available as an npm package.

Hash Collision Probability and Use Case Fit

Understanding the statistical properties of each hash function is essential for choosing the right tool and determining whether a collision would cause a correctness bug or just a cache miss.

Hash collision probability is a function of hash output size and the number of items being hashed. ohash produces a 64-character hexadecimal string (256-bit output from SHA-256 internally), giving a collision probability of approximately 1 in 2^128 for any two random inputs — astronomically low for all practical purposes. object-hash uses Node.js's crypto.createHash() with configurable algorithms; the default SHA1 (160-bit) has known theoretical weaknesses but is still collision-resistant for non-adversarial caching use cases. hash-wasm exposes the full algorithm selection including SHA-3 variants and BLAKE2, with hash sizes up to 512 bits for maximum collision resistance.

Adversarial resistance matters when hashes are used for security-sensitive purposes. Cache invalidation, content-addressable storage for developer tools, and change detection are non-adversarial: an attacker cannot influence the content being hashed. API request signing, file integrity verification, and password hashing are adversarial: a determined attacker may craft inputs designed to produce specific hash outputs. For adversarial use cases, cryptographic hash functions (SHA-256 or SHA-3, as provided by hash-wasm) are required; ohash and object-hash's fast non-cryptographic modes are explicitly inappropriate.

Determinism across environments is critical for distributed cache keys. ohash serializes JavaScript objects deterministically — { b: 2, a: 1 } and { a: 1, b: 2 } produce the same hash because ohash sorts object keys before hashing. object-hash similarly sorts keys by default. This determinism ensures that the same object produces the same hash whether computed in a Next.js server in US-East-1 or EU-West-1, making it safe to use ohash as a distributed cache key generator.

For high-performance scenarios where hashing thousands of objects per second matters (module bundlers, reactive frameworks checking for state changes), benchmark the specific object structures you'll hash. ohash's default mode is optimized for JSON-serializable objects and outperforms object-hash significantly for simple objects. hash-wasm's fastest algorithms (xxHash, MurmurHash3) outperform SHA-256 by 5-10x but at the cost of weaker collision resistance — appropriate for hash tables and caching, not for content integrity verification.

Methodology

Download data from npm registry (weekly average, February 2026). Feature comparison based on ohash v1.x, object-hash v3.x, and hash-wasm v4.x.

Compare hashing and cryptography packages on PkgPulse →

See also: AVA vs Jest and klona vs rfdc vs structuredClone, acorn vs @babel/parser vs espree.

The 2026 JavaScript Stack Cheatsheet

One PDF: the best package for every category (ORMs, bundlers, auth, testing, state management). Used by 500+ devs. Free, updated monthly.