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.
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.
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).// json-stable-stringify
// Internal dependency structure
const stringify = require('json-stable-stringify');
// Depends on: jsonify
json-stable-stringify-without-jsonify removes that dependency.
jsonify with native logic or safer alternatives.// 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.
// json-stringify-deterministic
// Internal dependency structure
const stringify = require('json-stringify-deterministic');
// Depends on: (none)
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.
cmp option to define custom sort logic.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.
cmp, space, and replacer options.// 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.
// 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}'
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.
// 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.
// 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.
// 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);
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.// webpack bundle analysis
// Includes: json-stable-stringify + jsonify
json-stable-stringify-without-jsonify is leaner.
jsonify cuts out dead weight.// webpack bundle analysis
// Includes: json-stable-stringify-without-jsonify
// Excludes: jsonify
json-stringify-deterministic is often the smallest.
// webpack bundle analysis
// Includes: json-stringify-deterministic only
Open source packages live or die by their maintenance. An unmaintained package is a security risk.
json-stable-stringify has sporadic updates.
// npm view json-stable-stringify time.modified
// Shows long gaps between releases
json-stable-stringify-without-jsonify is more active.
// npm view json-stable-stringify-without-jsonify time.modified
// Shows more recent activity
json-stringify-deterministic depends on the specific maintainer.
// npm view json-stringify-deterministic time.modified
// Check for recent commits
| Feature | json-stable-stringify | json-stable-stringify-without-jsonify | json-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 |
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.
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.
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.
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.
deterministic version of JSON.stringify() so you can get a consistent hash from stringified results
You can also pass in a custom comparison function.
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}
const stringify = require('json-stable-stringify')
Return a deterministic stringified string str from the object obj.
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.
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
}
The replacer parameter is a function opts.replacer(key, value) that behaves the same as the replacer
from the core JSON object.
With npm do:
npm install json-stable-stringify
MIT