Pretty-Print JSON in JavaScript and Python: Indent, Sort Keys, CLI
Quick answer
JavaScript: JSON.stringify(obj, null, 2), the third argument is the indent (a number up to 10, or a string such as '\t'). Python: json.dumps(obj, indent=2, sort_keys=True, ensure_ascii=False). Shell: python3 -m json.tool --indent 2 --sort-keys file.json. JavaScript has no built-in key sorting; use a replacer function.
JavaScript: JSON.stringify(value, replacer, space)
MDN defines space as the number of spaces to indent, clamped to 10, or a string whose first 10 characters are inserted before every nested level. Output from Node 20:
const obj = { name: 'bytepane', tags: ['json', 'tools'], nested: { b: 2, a: 1 }, empty: {} }
JSON.stringify(obj, null, 2)
// {
// "name": "bytepane",
// "tags": [
// "json",
// "tools"
// ],
// "nested": {
// "b": 2,
// "a": 1
// },
// "empty": {}
// }
JSON.stringify({ a: [1] }, null, 20) // indents by 10, not 20
// {
// "a": [
// 1
// ]
// }
// replacer ARRAY = allowlist of property names (applies at every level)
JSON.stringify(obj, ['name', 'nested', 'a'], 2)
// {
// "name": "bytepane",
// "nested": {
// "a": 1
// }
// }Sorted keys at every level
const sorted = (key, value) =>
value && typeof value === 'object' && !Array.isArray(value)
? Object.fromEntries(Object.keys(value).sort().map(k => [k, value[k]]))
: value
JSON.stringify(obj, sorted, 2)
// {
// "empty": {},
// "name": "bytepane",
// "nested": {
// "a": 1,
// "b": 2
// },
// "tags": [
// "json",
// "tools"
// ]
// }The replacer function is called for every key/value pair, including the root (key ""), so rebuilding each plain object with sorted keys sorts the whole tree. Arrays keep their order.
Python: json.dumps(obj, indent=2, sort_keys=True)
import json
obj = {"name": "bytepane", "tags": ["json", "tools"], "nested": {"b": 2, "a": 1}, "empty": {}, "city": "São Paulo"}
print(json.dumps(obj, indent=2))
# ...
# "empty": {},
# "city": "S\u00e3o Paulo" <- ensure_ascii=True is the default
# }
print(json.dumps(obj, indent=2, sort_keys=True, ensure_ascii=False))
# {
# "city": "São Paulo",
# "empty": {},
# "name": "bytepane",
# "nested": {
# "a": 1,
# "b": 2
# },
# "tags": [
# "json",
# "tools"
# ]
# }
print(json.dumps({"a": 1, "b": [1, 2]}, separators=(",", ":"))) # {"a":1,"b":[1,2]} compactsort_keys=True sorts recursively. ensure_ascii=False writes UTF-8 characters directly, which matches what JavaScript does by default.
Command line, no extra tools
$ echo '{"b":1,"a":{"y":2,"x":1},"s":"São"}' | python3 -m json.tool --sort-keys --indent 2 --no-ensure-ascii
{
"a": {
"x": 1,
"y": 2
},
"b": 1,
"s": "São"
}
$ echo '{"b":1,"a":[1,2]}' | node -e "process.stdin.on('data', d => console.log(JSON.stringify(JSON.parse(d), null, 2)))"
{
"b": 1,
"a": [
1,
2
]
}python3 -m json.tool also accepts --tab, --compact, and an output file argument; run it with --help for your Python version.
Reference table
| Task | JavaScript | Python |
|---|---|---|
| Indent 2 spaces | JSON.stringify(obj, null, 2) | json.dumps(obj, indent=2) |
| Indent with tabs | JSON.stringify(obj, null, '\t') | json.dumps(obj, indent='\t') |
| Maximum indent | Clamped to 10 spaces / 10 chars | No limit |
| Sort keys | Replacer function (see below) | sort_keys=True |
| Keep only some keys | Replacer array: ['name', 'id'] | Filter the dict first |
| Non-ASCII characters | Kept as-is (UTF-8) | Escaped (\u00e3) unless ensure_ascii=False |
| Compact output | JSON.stringify(obj) | json.dumps(obj, separators=(',', ':')) |
| undefined / functions | Omitted in objects, null in arrays | TypeError: not JSON serializable |
| NaN / Infinity | Written as null | Written as NaN / Infinity (invalid JSON) unless allow_nan=False |
| BigInt / int | BigInt throws TypeError | int of any size is fine |
| Command line | node -e (see below) | python3 -m json.tool --indent 2 --sort-keys |
Values that silently change or throw
// JavaScript (Node 20)
JSON.stringify({ u: undefined, f() {}, s: Symbol('x'), n: NaN, i: Infinity, d: new Date(0), arr: [undefined, () => {}] })
// {"n":null,"i":null,"d":"1970-01-01T00:00:00.000Z","arr":[null,null]}
// ^ u, f, s omitted; NaN/Infinity -> null; Date -> toISOString(); array holes -> null
JSON.stringify({ big: 10n })
// TypeError: Do not know how to serialize a BigInt# Python 3.10
json.dumps({"n": float('nan'), "i": math.inf})
# '{"n": NaN, "i": Infinity}' <- not valid JSON; JSON.parse('NaN') throws in JavaScript
json.dumps({"n": float('nan')}, allow_nan=False)
# ValueError: Out of range float values are not JSON compliantPython's json.loads accepts NaN on the way back in, which is why Python-to-Python round trips hide the problem until a JavaScript client or a strict validator sees the payload.
Primary sources: MDN JSON.stringify(), Python json module, RFC 8259.
Frequently Asked Questions
How do I pretty-print JSON with 4 spaces instead of 2?
JavaScript: JSON.stringify(obj, null, 4). Python: json.dumps(obj, indent=4). Command line: python3 -m json.tool --indent 4 file.json. In JavaScript the space argument is clamped to 10, so JSON.stringify(obj, null, 20) still indents by 10 spaces.
How do I sort JSON keys alphabetically?
Python has it built in: json.dumps(obj, sort_keys=True) or python3 -m json.tool --sort-keys. JavaScript has no flag; pass a replacer function to JSON.stringify that rebuilds each plain object from Object.keys(value).sort(), which sorts every nesting level.
Why does JSON.stringify drop some of my properties?
Per MDN, undefined, functions and symbols are not valid JSON values: JSON.stringify omits them when they are object properties and writes null when they are array elements. NaN and Infinity become null. BigInt values throw a TypeError unless you provide a replacer or a BigInt.prototype.toJSON.
Why does Python output NaN in my JSON?
json.dumps defaults to allow_nan=True, which emits the JavaScript literals NaN, Infinity and -Infinity. They are not valid JSON per RFC 8259, and JSON.parse in JavaScript rejects them. Pass allow_nan=False to get a ValueError instead of invalid output, and clean the data before serializing.
Try it in the browser
- JSON Formatter, pretty-print and validate JSON without leaving the browser.
- JSON Minifier, the reverse operation for payloads and config bundles.