A worked example: a minified event feed
APIs send JSON minified - no spaces, no line breaks - because it is what machines want. Here is a page of payment events as it arrives, all on one line:
{"cursor":"eyJwIjoyfQ","events":[{"id":1839274619283746817,"type":"order.paid","amount":12.50,"currency":"EUR","createdAt":"2026-09-29T10:15:04Z","tags":[]},{"id":1839274619283746818,"type":"order.refunded","amount":8.00,"currency":"EUR","createdAt":"2026-09-29T10:17:41Z","tags":["partial"],"meta":{}}],"hasMore":true}
Formatted with a 2-space indent:
{
"cursor": "eyJwIjoyfQ",
"events": [
{
"id": 1839274619283746817,
"type": "order.paid",
"amount": 12.50,
"currency": "EUR",
"createdAt": "2026-09-29T10:15:04Z",
"tags": []
},
{
"id": 1839274619283746818,
"type": "order.refunded",
"amount": 8.00,
"currency": "EUR",
"createdAt": "2026-09-29T10:17:41Z",
"tags": [
"partial"
],
"meta": {}
}
],
"hasMore": true
}
Empty containers stay compact as [] and {} rather than spreading over two lines, and every value is character-for-character what was in the input. That last part is worth dwelling on, because most formatters do not guarantee it.
A formatter should never change your data
The easy way to write a JSON formatter in JavaScript is two calls: JSON.parse the text, then JSON.stringify it back with indentation. Plenty of online formatters work that way. Here is what that approach does to the example above:
"id": 1839274619283746800,
"amount": 12.5,
...
"id": 1839274619283746800,
"amount": 8,
Both events now have the same ID. JavaScript numbers are 64-bit floats and are exact only up to 253, so 1839274619283746817 and 1839274619283746818 both round to 1839274619283746800. Nothing warns you. If you copy that "formatted" JSON into a bug report or a test fixture, you have quietly replaced two real IDs with one wrong one. The amounts lose their trailing zeros too - harmless to a parser, but no longer the bytes the API sent, which matters when you are debugging a signature or a checksum.
This formatter never re-serialises. It checks the text with JSON.parse to find errors, then walks the original text and changes nothing but the whitespace between tokens. Numbers, string escapes like é, key order, and even duplicate keys are left exactly as they were.
Command-line tools vary, so it is worth knowing what yours does. Each of these was run on a document containing "id": 1839274619283746817, "amount": 12.50, and a duplicated key:
| Formatter | Large integer | 12.50 | Duplicate key |
|---|---|---|---|
| This page | Kept exactly | Kept as 12.50 | Both kept |
jq . (jq 1.7) | Kept exactly | Kept as 12.50 | Last one kept |
python3 -m json.tool (3.12) | Kept exactly | Becomes 12.5 | Last one kept |
JSON.stringify(JSON.parse(text)) (Node.js 24) | Rounded to ...746800 | Becomes 12.5 | Last one kept |
jq earns a mention: versions before 1.7 converted every number to a float and rounded large integers the same way JavaScript does. If you rely on jq to pretty-print IDs, check jq --version. For more on where JSON values break between languages, see What is JSON?, which tests these cases in Node.js and Python.
Validation comes with it
Because the text is parsed before it is formatted, invalid JSON is caught with a specific message rather than a garbled result. The most common mistake - a comma after the last item, which JavaScript and most config formats allow but JSON does not - is located exactly. Given:
{
"id": 1,
"tags": ["a", "b",],
"ok": true
}
the tool reports:
Trailing comma before "]" (line 3, column 20). JSON does not allow a comma after the last item.
Other errors report where the browser's parser gave up, so the wording varies between browsers. In Chrome, for example, a missing comma between two properties gives Expected ',' or '}' after property value in JSON at position 14 (line 3 column 3) - the position is where the parser noticed the problem, which is usually just after the actual mistake - look at the end of the previous line first.
Single-quoted strings, comments, and unquoted keys are all rejected too. They are valid in JavaScript and in JSON5, but not in JSON, and an API or JSON.parse on the other end will reject them just the same.
What indentation costs
Formatted JSON is for people; minified JSON is for everything else. To put a number on the difference, I formatted the 50,000-order dataset from the JSON vs XML comparison each way and compressed the result with gzip at level 9 (Node.js zlib):
| Layout | Size | vs minified | gzipped | vs minified |
|---|---|---|---|---|
| Minified | 7.81 MB | - | 0.96 MB | - |
| Tab indent (for reference) | 12.76 MB | +63% | 1.01 MB | +5% |
| 2-space indent | 16.21 MB | +108% | 1.05 MB | +9% |
| 4-space indent | 23.12 MB | +196% | 1.08 MB | +12% |
Indentation more than doubles the raw size of deeply nested data, because every line of a nested record repeats its indent. Compression takes most of that back, so for an HTTP response served with gzip, pretty-printing costs about a tenth more on the wire. Minifying matters most where nothing compresses the data: values stored in a database column or browser localStorage, message queues with a size limit, environment variables, and log lines.
Logs deserve a special mention. NDJSON - one JSON document per line, used by most log shippers and bulk APIs - requires minified records, because a newline is what separates one record from the next. A pretty-printed record there is not just larger; it breaks the file.
2 spaces or 4?
Neither is more correct, so match what the file will live next to. jq and Prettier indent with 2 spaces by default, and 2 is the common choice in JavaScript and TypeScript projects. Python's json.tool uses 4, as does a lot of Python code that calls json.dumps(data, indent=4). For a file committed to a repository, whatever the project's formatter produces is the right answer, because it keeps diffs down to real changes.
What the formatter does not do
- It does not sort keys. Key order is preserved as part of leaving your data untouched. To compare two documents with keys in different orders, use
jq -S .on both first. - It does not repair invalid JSON. It tells you where the problem is, but guessing a fix - which quote was meant to close which string - is a good way to hide a real bug.
- It is not built for very large files. File uploads are capped at 5 MB, and a formatted document of that size can make the output box slow to scroll. For big files,
jq .on the command line is the better tool.
Other output formats
The same box can do more than reformat. Switch the format to see the structure instead of the text:
- View JSON as an indented tree - the shape without the values, which stays short however long the data is
- Flatten JSON to dot notation - every key path as a flat list
- Convert JSON to a TypeScript interface
- Generate JSONPath expressions
Further reading
- What is JSON? - including the values that do not survive a round trip between languages
- Processing large JSON files - when a file is too big to format in a browser
- JSON Schema: a practical guide to validating JSON - checking structure, not just syntax