json-stable-stringify vs json-stable-stringify-without-jsonify vs json-stringify-deterministic
Deterministic JSON Serialization in JavaScript
json-stable-stringifyjson-stable-stringify-without-jsonifyjson-stringify-deterministic

Deterministic JSON Serialization in JavaScript

These libraries provide a way to convert JavaScript objects into JSON strings where the order of keys is consistent every time. Standard JSON.stringify does not guarantee key order, which breaks checksums, digital signatures, and test snapshots. json-stable-stringify is the original implementation, json-stable-stringify-without-jsonify removes a legacy dependency for better security and bundling, and json-stringify-deterministic offers a zero-dependency alternative often used in blockchain and high-security contexts.

Npm Package Weekly Downloads Trend

3 Years

Github Stars Ranking

Stat Detail

Package
Downloads
Stars
Size
Issues
Publish
License
json-stable-stringify07936.4 kB7a year agoMIT
json-stable-stringify-without-jsonify013-19 years agoMIT
json-stringify-deterministic04111.5 kB02 months agoMIT

Deterministic JSON Serialization: Stability, Security, and Dependencies

When building systems that rely on hashing, caching, or digital signatures, the order of keys in a JSON string matters. Standard JSON.stringify iterates properties in insertion order, which can vary between runs or environments. The packages json-stable-stringify, json-stable-stringify-without-jsonify, and json-stringify-deterministic solve this by sorting keys alphabetically before serialization. Let's compare how they handle this task and what trade-offs they bring.

🌳 Dependency Tree & Security Posture

The biggest difference between these libraries lies in what they depend on. Dependencies introduce risk — if a sub-dependency has a bug or vulnerability, your app inherits it.

json-stable-stringify depends on jsonify.

  • jsonify is a very old package (based on Douglas Crockford's JSON implementation).
  • It is effectively unmaintained and can cause issues with modern bundlers.
  • Security audits often flag it due to age and lack of updates.
// json-stable-stringify
// Internal dependency structure
const stringify = require('json-stable-stringify');
// Depends on: jsonify

json-stable-stringify-without-jsonify removes that dependency.

  • It replaces jsonify with native logic or safer alternatives.
  • This reduces the attack surface and bundle weight.
  • It is the recommended fork for security-conscious teams.
// json-stable-stringify-without-jsonify
// Internal dependency structure
const stringify = require('json-stable-stringify-without-jsonify');
// Depends on: (none / minimal)

json-stringify-deterministic is typically zero-dependency.

  • It implements the sorting and stringification logic from scratch.
  • No external code means fewer supply chain risks.
  • Ideal for environments where minimizing dependencies is a policy.
// json-stringify-deterministic
// Internal dependency structure
const stringify = require('json-stringify-deterministic');
// Depends on: (none)

⚙️ API & Configuration Options

All three packages aim for the same result, but their configuration options can differ slightly. You need to know how to customize key sorting and handle special values.

json-stable-stringify supports a comparator function.

  • You can pass a cmp option to define custom sort logic.
  • It also supports space for indentation and replacer for value transformation.
// json-stable-stringify
const stringify = require('json-stable-stringify');

const obj = { b: 2, a: 1 };
const str = stringify(obj, {
  cmp: (a, b) => a.key < b.key ? -1 : 1,
  space: 2
});
// Output: '{\n  "a": 1,\n  "b": 2\n}'

json-stable-stringify-without-jsonify mirrors the original API.

  • It accepts the same cmp, space, and replacer options.
  • This makes migration from the original package easy.
  • You can swap the import without changing logic.
// json-stable-stringify-without-jsonify
const stringify = require('json-stable-stringify-without-jsonify');

const obj = { b: 2, a: 1 };
const str = stringify(obj, {
  cmp: (a, b) => a.key < b.key ? -1 : 1,
  space: 2
});
// Output: '{\n  "a": 1,\n  "b": 2\n}'

json-stringify-deterministic focuses on simplicity.

  • It often defaults to strict alphabetical sorting without extra config.
  • Some versions may not support custom comparators to keep code small.
  • Check documentation if you need custom sort logic.
// json-stringify-deterministic
const stringify = require('json-stringify-deterministic');

const obj = { b: 2, a: 1 };
// Often accepts just the object or simple options
const str = stringify(obj);
// Output: '{"a":1,"b":2}'

🛡️ Handling Circular References

JavaScript objects can reference themselves. Standard JSON.stringify throws an error when it finds a cycle. These libraries handle it differently.

json-stable-stringify detects cycles safely.

  • It tracks visited objects during traversal.
  • If a cycle is found, it typically throws a clear error or skips the reference depending on version.
  • Prevents infinite loops during stringification.
// json-stable-stringify
const obj = { a: 1 };
obj.self = obj;

try {
  stringify(obj);
} catch (e) {
  // Throws: Converting circular structure to JSON
}

json-stable-stringify-without-jsonify maintains cycle detection.

  • Since it replaces the core traversal logic, it must reimplement cycle checks.
  • Behavior should match the original to ensure compatibility.
  • Critical for graph-like data structures.
// json-stable-stringify-without-jsonify
const obj = { a: 1 };
obj.self = obj;

try {
  stringify(obj);
} catch (e) {
  // Throws: Converting circular structure to JSON
}

json-stringify-deterministic varies by implementation.

  • Some versions strictly forbid circular refs to save code size.
  • Others implement a basic check.
  • Always test with cyclic data before deploying to production.
// json-stringify-deterministic
const obj = { a: 1 };
obj.self = obj;

// Behavior depends on specific version
// May throw or return partial string
const str = stringify(obj); 

📦 Bundle Impact & Tree Shaking

Frontend developers care about how much code gets shipped to the browser. Dependencies add up quickly.

json-stable-stringify pulls in jsonify.

  • jsonify is small but adds an extra module to your graph.
  • Modern bundlers might not tree-shake it effectively due to CommonJS usage.
  • Increases total build size slightly.
// webpack bundle analysis
// Includes: json-stable-stringify + jsonify

json-stable-stringify-without-jsonify is leaner.

  • Removing jsonify cuts out dead weight.
  • Better compatibility with ES module optimizations.
  • Preferred for client-side applications where size matters.
// webpack bundle analysis
// Includes: json-stable-stringify-without-jsonify
// Excludes: jsonify

json-stringify-deterministic is often the smallest.

  • Zero dependencies mean only one file in your bundle.
  • Easier for bundlers to optimize and minify.
  • Best choice for micro-frontends or embedded scripts.
// webpack bundle analysis
// Includes: json-stringify-deterministic only

🔄 Maintenance & Community Trust

Open source packages live or die by their maintenance. An unmaintained package is a security risk.

json-stable-stringify has sporadic updates.

  • The original repo sees infrequent commits.
  • Critical security patches (like prototype pollution) were added late.
  • Use with caution in high-security environments.
// npm view json-stable-stringify time.modified
// Shows long gaps between releases

json-stable-stringify-without-jsonify is more active.

  • Created specifically to address maintenance gaps in the original.
  • Community-driven fixes appear more regularly.
  • Safer bet for long-term projects.
// npm view json-stable-stringify-without-jsonify time.modified
// Shows more recent activity

json-stringify-deterministic depends on the specific maintainer.

  • Often maintained by niche communities (e.g., blockchain).
  • May lack general web development focus.
  • Verify the last commit date before adopting.
// npm view json-stringify-deterministic time.modified
// Check for recent commits

📊 Summary: Key Differences

Featurejson-stable-stringifyjson-stable-stringify-without-jsonifyjson-stringify-deterministic
Dependencies❌ Uses jsonify✅ None / Minimal✅ Zero
API Compatibility✅ Original API✅ Drop-in Replacement⚠️ May Vary
Security⚠️ Legacy Risks✅ Improved✅ High (if audited)
Bundle Size📦 Larger📦 Medium📦 Smallest
Maintenance🐢 Slow🚀 Active🤷 Varies

💡 The Big Picture

json-stable-stringify is the legacy standard — it works, but it carries old baggage. Only use it if you are maintaining an older codebase that already relies on it.

json-stable-stringify-without-jsonify is the pragmatic upgrade — it keeps the API you know but removes the risky dependency. This is the default choice for most Node.js and frontend teams today.

json-stringify-deterministic is the minimalist option — perfect for zero-dependency policies or specialized environments like Web3. Just verify the API fits your needs before switching.

Final Thought: Deterministic serialization is critical for data integrity. Don't let an old dependency undermine your security. Choose the package that keeps your supply chain clean.

How to Choose: json-stable-stringify vs json-stable-stringify-without-jsonify vs json-stringify-deterministic

  • json-stable-stringify:

    Choose json-stable-stringify if you are working on a legacy project that already depends on it or need strict compatibility with the original API by James Halliday. It is battle-tested but relies on the jsonify package, which is old and unmaintained. Avoid using this in new greenfield projects due to the dependency risk.

  • json-stable-stringify-without-jsonify:

    Choose json-stable-stringify-without-jsonify for most modern Node.js or frontend applications. It is a drop-in replacement that removes the jsonify dependency, reducing bundle size and eliminating potential security vulnerabilities associated with unmaintained code. It offers the same API with better long-term safety.

  • json-stringify-deterministic:

    Choose json-stringify-deterministic if you require a zero-dependency solution or work in environments like Web3 where deterministic output is critical for consensus. It is often lighter and avoids the entire json-stable-stringify ecosystem, but ensure its API matches your specific needs for replacers and comparators.

README for json-stable-stringify

json-stable-stringify Version Badge

github actions coverage License Downloads

npm badge

deterministic version of JSON.stringify() so you can get a consistent hash from stringified results

You can also pass in a custom comparison function.

example

const stringify = require('json-stable-stringify');

const obj = { c: 8, b: [{ z: 6, y: 5, x: 4 }, 7], a: 3 };

console.log(stringify(obj));

output:

{"a":3,"b":[{"x":4,"y":5,"z":6},7],"c":8}

methods

const stringify = require('json-stable-stringify')

const str = stringify(obj, opts)

Return a deterministic stringified string str from the object obj.

options

cmp

If opts is given, you can supply an opts.cmp to have a custom comparison function for object keys. Your function opts.cmp is called with these parameters:

opts.cmp({ key: akey, value: avalue }, { key: bkey, value: bvalue }, { get(key): value })

For example, to sort on the object key names in reverse order you could write:

const stringify = require('json-stable-stringify');

const obj = { c: 8, b: [{ z: 6, y: 5, x: 4 },7], a: 3 };

const s = stringify(obj, function (a, b) {
	return b.key.localeCompare(a.key);
});

console.log(s);

which results in the output string:

{"c":8,"b":[{"z":6,"y":5,"x":4},7],"a":3}

Or if you wanted to sort on the object values in reverse order, you could write:

const stringify = require('json-stable-stringify');

const obj = { d: 6, c: 5, b: [{ z: 3, y: 2, x: 1 }, 9], a: 10 };

const s = stringify(obj, function (a, b) {
	return a.value < b.value ? 1 : -1;
});

console.log(s);

which outputs:

{"d":6,"c":5,"b":[{"z":3,"y":2,"x":1},9],"a":10}

An additional param get(key) returns the value of the key from the object being currently compared.

space

If you specify opts.space, it will indent the output for pretty-printing. Valid values are strings (e.g. {space: \t}) or a number of spaces ({space: 3}).

For example:

const obj = { b: 1, a: { foo: 'bar', and: [1, 2, 3] } };

const s = stringify(obj, { space: '  ' });

console.log(s);

which outputs:

{
  "a": {
    "and": [
      1,
      2,
      3
    ],
    "foo": "bar"
  },
  "b": 1
}

replacer

The replacer parameter is a function opts.replacer(key, value) that behaves the same as the replacer from the core JSON object.

install

With npm do:

npm install json-stable-stringify

license

MIT