JSON Minification: What It Saves and What It Does Not
Minifying JSON means removing the whitespace that sits between tokens: indentation, line breaks and the space after each colon and comma. The data is unchanged. What is less obvious is how much that actually saves once compression is involved, and when a single-line document is a requirement, not an optimization.
What counts as removable whitespace
The JSON grammar (RFC 8259) allows four whitespace characters between tokens: space, tab, line feed and carriage return. They carry no meaning, so all of them can go. Whitespace inside a string is data and must be kept, which is why minifying with a regular expression is a bad idea: a pattern that strips spaces cannot tell "New York" from indentation.
{
"city": "New York",
"tags": [
"a",
"b"
],
"active": true
}{"city":"New York","tags":["a","b"],"active":true}How much smaller, measured
To put numbers on it, we generated a document with 200 user records (id, name, email, a boolean, a two-item array and a nested address object) and measured it in several forms with Node.js.
Two things stand out:
- Uncompressed, minifying is a big win. The minified file is 44% smaller than the 2-space version and 58% smaller than the 4-space version. Deeply nested data saves even more, because indentation grows with depth.
- After compression, the gap almost disappears. With gzip the difference is 266 bytes (about 9%). Repeated runs of spaces are the easiest thing in the world for a compressor to encode.
So if your API already serves responses with gzip or Brotli, minifying mainly saves a little CPU and memory for parsing, not much bandwidth. The cases where it matters are the ones where nothing compresses the data for you.
When one-line JSON actually matters
- JSON Lines / NDJSON. These formats store one JSON value per line, so each record must be on a single line. The same goes for most structured log pipelines, which split entries on newlines.
- Environment variables and CLI arguments. Multi-line values are awkward to quote in shells,
.envfiles and CI settings. A one-line value avoids a whole class of quoting bugs. - Storage that is not compressed. Rows in a text column, cache entries, and browser
localStorage(limited to roughly 5 MB per origin) store exactly the characters you give them. - Embedding JSON in other things. HTML attributes, URL parameters, config templates and test fixtures are easier to handle when the value has no line breaks.
- LLM prompts. Indentation and newlines are tokens too. Minifying a large JSON input before sending it to a model reduces the bill; see the TOON article for going further.
What a minifier can change by accident
Most minifiers, including the one on this site and JSON.stringify(JSON.parse(text)), do not edit the text. They parse it into values and print those values again. That is safe for whitespace, but anything the parser cannot represent exactly comes out different.
| Input | After parse + print in JavaScript | Why |
|---|---|---|
9007199254740993 | 9007199254740992 | Integers above 2^53 are not exact in a 64-bit float |
1.50 | 1.5 | Trailing zeros are not part of the number |
1e400 | null | Overflows to Infinity, which JSON cannot express |
{"a":1,"a":2} | {"a":2} | Duplicate keys: the last one wins |
{"b":1,"2":0,"1":0} | {"1":0,"2":0,"b":1} | Integer-like keys are moved first, in ascending order |
None of this affects typical data. It matters when the document carries large numeric IDs (send those as strings), money with fixed decimals, or when the exact bytes are signed or hashed. For those, minify with a tool that works on the token stream, or keep the original.
Minifying from the command line and in code
# jq: -c prints compact output
jq -c . data.json > data.min.json
# Python 3.9+
python -m json.tool --compact data.json
# Node.js
node -e "process.stdout.write(JSON.stringify(JSON.parse(require('fs').readFileSync(0))))" < data.jsonIn code, watch the defaults. JavaScript's JSON.stringify(value) is compact unless you pass an indent argument. Python's json.dumps(value) is not: it puts a space after each comma and colon, so compact output needs explicit separators.
json.dumps(value) # {"a": 1, "b": [1, 2]}
json.dumps(value, separators=(",", ":")) # {"a":1,"b":[1,2]}