Minified JSON is great for the wire and terrible for your eyes. If you've ever stared at a single-line API response trying to find which bracket closed too early, you already know why formatting matters. This is a quick, practical rundown of what JSON formatting actually does, when you need it, and the mistakes that bite people.

What formatting JSON actually means

Formatting (or "beautifying") JSON is the process of taking a valid JSON string and re-rendering it with consistent indentation, line breaks, and spacing so a human can read it. The data doesn't change — only the whitespace does. Minifying is the reverse: strip every non-essential character to shrink the payload.

Here's the same object both ways. Minified:

{"id":42,"name":"Ada","roles":["admin","dev"],"active":true}

Formatted with two-space indentation:

{
  "id": 42,
  "name": "Ada",
  "roles": [
    "admin",
    "dev"
  ],
  "active": true
}

Same bytes semantically. Night-and-day difference when you're debugging.

When you actually reach for it

You don't format JSON that's heading out over HTTP — leave that minified. You format JSON when a human needs to read or edit it. In practice that's:

  • Debugging API responses. You copy a raw response from the network tab and it's one unbroken line. Format it to see the shape.
  • Reviewing config files. package.json, tsconfig.json, and friends live in version control. Consistent formatting means clean diffs.
  • Writing fixtures and test data. Readable mock data is easier to maintain and spot-check.
  • Comparing two payloads. If both are formatted with the same rules, a line-based diff tool tells you exactly what changed.

Doing it in code

Most languages ship this out of the box. In JavaScript, JSON.stringify takes an indentation argument as its third parameter:

const data = JSON.parse(raw);
console.log(JSON.stringify(data, null, 2));

The null is the replacer (skip it), and 2 is the number of spaces to indent. Pass "\t" instead if your team prefers tabs. In Python it's json.dumps(data, indent=2). On the command line, jq . or python -m json.tool both work.

When you just need a quick, no-setup view — say you've pasted a response from a log and don't want to open a REPL — a browser-based JSON Beautifier handles it instantly and also validates the input as a bonus. I keep one bookmarked for exactly those throwaway moments.

Common gotchas

It must be valid JSON first

Formatting tools parse the input before re-emitting it. If your JSON is invalid, it won't format — it'll throw an error. That's actually useful: a failed format is a quick validity check. The usual culprits:

  • Trailing commas. {"a": 1,} is valid in JavaScript objects but illegal in JSON. This one catches people constantly.
  • Single quotes. JSON requires double quotes on both keys and string values. {'a': 1} is not JSON.
  • Unquoted keys. {a: 1} works in JS but not JSON.
  • Comments. Standard JSON has no comments. Things like tsconfig.json use JSONC, a superset — a strict JSON parser will reject the // lines.

Key order isn't guaranteed

The JSON spec treats objects as unordered. Most parsers preserve insertion order in practice, but don't build logic that depends on it. If you need a stable, diff-friendly output, sort keys explicitly — Python's json.dumps has sort_keys=True, and many tools offer the option.

Number precision

JSON numbers are doubles as far as most parsers care. Large integers like 64-bit IDs can lose precision when they round-trip through JavaScript. If you've got IDs beyond Number.MAX_SAFE_INTEGER, serialize them as strings. Formatting won't fix a precision bug that already happened — but a formatted view makes it easier to spot the mangled value.

Encoding and escaped characters

Non-ASCII characters and escape sequences (\n, \u00e9) are perfectly valid, but they can make nested JSON-inside-a-string confusing. If a field holds a stringified JSON blob, you may need to parse it a second time to read the inner structure.

A short worked example

Say an endpoint hands you this blob from the network tab:

{"status":"ok","data":{"user":{"id":42,"name":"Ada","settings":{"theme":"dark","notifications":false}},"permissions":["read","write"]},"meta":{"ts":1718000000}}

You suspect notifications is coming back wrong. Scanning that line by eye is painful. Format it:

{
  "status": "ok",
  "data": {
    "user": {
      "id": 42,
      "name": "Ada",
      "settings": {
        "theme": "dark",
        "notifications": false
      }
    },
    "permissions": [
      "read",
      "write"
    ]
  },
  "meta": {
    "ts": 1718000000
  }
}

Now the nesting is obvious: notifications lives under data.user.settings, and it's false. Thirty seconds instead of squinting at a wall of text. That's the whole value proposition.

A few habits worth keeping

  • Standardize indentation across your repo. Pick two spaces or tabs and enforce it with Prettier or an .editorconfig. Mixed indentation produces noisy diffs.
  • Don't commit minified JSON to source control unless it's a build artifact. Formatted files review better.
  • Format before you diff, not after. Comparing a minified payload against a formatted one gives you a useless diff.
  • Be careful pasting sensitive data into any online tool. For anything containing secrets or production PII, use a local command or a client-side tool that doesn't send data to a server.

JSON formatting is a tiny skill with an outsized payoff. It won't make your architecture better, but it will make the hour you spend debugging a weird payload far less miserable. Keep a formatter close, learn the invalid-JSON gotchas, and let the tools do the indenting.

Photo by Valery Sysoev on Unsplash