Understanding JSON formatting
JSON carries no formatting of its own. Whitespace between tokens has no meaning at all, so the same document can arrive as one unbroken line or spread across a thousand indented ones, and every parser reads them identically.
{"id":42,"tags":["a","b"]} → the same document over seven indented lines — whitespace is the only difference, and no parser can tell
So a formatter is really two tools sharing a parser: beautifying inserts line breaks and indentation so structure becomes visible, minifying strips every optional byte so the document costs as little as possible to send, and neither changes what the document says. That parser gives you the third answer for free — when a document does not match the grammar the useful reply is not “invalid” but the line and column where matching stopped, and what was expected there instead. All of it runs on your device, and nothing you paste is uploaded.
Why beautify, and why minify
Most JSON that reaches a person came from a machine, and machines emit the compact form. An API response, a log line, a config value pulled out of an environment variable: all of them arrive as a single line that is impossible to read past the first nesting level. Beautifying is how you find the one field you came for.
Minifying runs the other way, and the wins are real but bounded. Indentation is often a fifth to a third of a pretty-printed document, so stripping it is worth doing on anything that crosses a network many times a day. It is not worth doing on anything a person will read, and it is pointless on a response that is already gzipped, where the compressor removes most of that redundancy anyway.
The honest rule is that formatting follows the audience. Files a human edits — configuration, fixtures, seed data — stay beautified and diff cleanly line by line. Payloads only a program reads get minified. Nothing about correctness changes either way, which is exactly why this is a preference rather than a decision to agonise over.
Indentation templates
The template decides one thing: what one level of nesting costs. Everything else about the output is the same.
| Template | One level looks like | Best for |
|---|---|---|
| 2 spaces | Two spaces | The default nearly everywhere: JavaScript tooling, npm manifests, most API examples |
| 3 spaces | Three spaces | Matching a house style that already uses it; rare outside legacy codebases |
| 4 spaces | Four spaces | Deeply nested documents where two spaces stop being distinguishable, and Python-adjacent projects |
| Tab | One tab character | Letting each reader choose their own width, and any repository whose editor config uses tabs |
| Minified | Nothing at all | Payloads on the wire, embedded strings, and anything a person will not read |
Minifying is a separate action rather than a fifth template, because it is not an indentation choice — it removes the line breaks too. Switch the action at the top of the tool and the indentation control goes quiet, since there is nothing left for it to act on.
Four specifications, and what actually differs
JSON has been standardised four times, which sounds worse than it is. Three of the four describe the same language; only one of them is genuinely stricter, and it is the oldest.
| Specification | Top-level value | What that changes here |
|---|---|---|
| RFC 8259 | Any value | The default. Additionally refuses a string carrying half of a surrogate pair, because that cannot be encoded as UTF-8. |
| RFC 7159 | Any value | Lifted RFC 4627's restriction on the top level. A lone surrogate is reported as a warning rather than an error. |
| RFC 4627 | Object or array only | The original definition. A bare string, number, boolean or null at the top level is rejected as not being a JSON text. |
| ECMA-404 | Any value | Published alongside RFC 7159 to describe the same grammar, so this tool treats the two identically. |
If you are unsure, leave it on RFC 8259 — it is what every current parser implements and what “JSON” means in 2026. Reach for RFC 4627 only when you are targeting something genuinely old, such as a strict validator written against the 2006 definition, and want to know in advance that a bare value will be refused.
Every option, and when to use it
The defaults — beautify at two spaces, RFC 8259, repair off, document order, output as-is — are what you want most of the time. The rest exist for the cases where the document is broken, the receiving system is stricter, or the output has to survive a narrower channel.
| Option | What it does | Reach for it when |
|---|---|---|
| Indentation | Sets what one nesting level costs: two, three or four spaces, or a tab. | The output goes into a repository whose style differs from the two-space default. |
| Specification | Chooses which published grammar the document is held to. | You are targeting a parser written against an older definition, or want a lone surrogate treated as fatal. |
| Repair | Accepts the usual non-standard extras — trailing commas, comments, bare keys, single and curly quotes, NaN and Infinity, hex and malformed numbers — and writes valid JSON out. | You pasted something that came from a config file, a Python REPL, or a document someone edited by hand. |
| Sort keys | Reorders every object's members by name, all the way down. | You are about to diff two documents, or want a stable order for a checksum or a fixture. |
| Escape non-ASCII | Rewrites every character above ASCII as a \uXXXX escape. | The output travels through something that mangles UTF-8 — an old logging pipeline, a terminal paste, a header value. |
The controls constrain each other, and the interface follows suit rather than lying about it. Minifying removes every space, so the indentation control greys out. Validating reports on the document instead of rewriting it, so indentation, key sorting and Unicode escaping all go quiet — none of them would have anything to act on.
Repair deserves one caveat. It only touches things whose intent is unambiguous: a comma before a closing bracket can only be a trailing comma, and a bare word where a key belongs can only be an unquoted key. It will not guess at an unterminated string, because the closing quote could belong anywhere, and it will not invent a missing bracket. When it does change something, the report says exactly what it changed and how many times.
The output panel has two controls of its own. Search, from the magnifier or Ctrl+F while the output has focus, highlights every match as you type, steps through them with Enter and Shift+Enter, and opens any folded section a match is hiding in — the browser’s own find cannot help here, because only the lines on screen are drawn. Full screen moves the same output, with its folds and search intact, over the whole window; Escape brings it back.
Reading an error
Every failure comes back with a line and a column, counted the way you would count them: from one, in characters. That last part matters more than it sounds. An emoji earlier in the line is one character to you but two units to the engine underneath, and a formatter that counts the engine's way reports every later column one place too far along.
The message names what the grammar wanted and what it found instead, because “unexpected token” on its own sends you reading the whole document. Knowing that a colon was expected at line 12, column 9, and a comma turned up, usually points straight at the missing quote two lines above.
When the problem is one Repair understands, the message says so rather than leaving you to guess. Trailing commas, comments, bare keys, non-standard quotes and NaN all carry that hint; unterminated strings and missing brackets do not, because no automatic fix for them would be safe.
How our formatter works
Everything on this page runs on your device. The document is read by a parser written for this tool rather than handed to the browser's own JSON.parse, and there is no server call and nothing stored.
That choice buys three things. JSON.parse reports failures through an error message that every engine words differently — some carry a line and column, some do not — so a formatter built on it gives a different answer in Chrome than in Safari. It also has no lenient mode, which is what Repair needs. And it routes every number through a double, which silently rounds any integer past 9,007,199,254,740,992; keeping the literal exactly as written is the only way a formatter can promise it did not quietly change your data.
The rest follows from that. Strings are decoded once and re-escaped canonically on the way out, so the output is byte-identical whichever escapes the input happened to use. A byte order mark at the start is ignored, as the specification permits. Nesting is capped at 512 levels and input at one megabyte, both reported as ordinary messages rather than a frozen tab.
Use cases
Reading an API response is the common one. Paste the single line your terminal printed, beautify it, and the shape you were looking for is visible immediately — including which fields are null and which are missing altogether.
Fixing a config file someone hand-edited is the second. A trailing comma in a tsconfig, a comment in a package manifest, smart quotes pasted out of a chat message: turn on Repair, and the output is the same document with the damage undone and a list of what changed.
Beyond that: sorting keys before diffing two fixtures so the diff shows real changes rather than reordering, minifying a payload before embedding it in a source file, escaping non-ASCII so a document survives a pipeline that mangles UTF-8, and checking whether a large integer id is one your consumers will round.
Frequently asked questions
File API and everything is parsed and written on your device. The page makes no network request while formatting, and nothing you paste, open, or download is transmitted.JSON.parse cannot: any integer past 9,007,199,254,740,992 loses precision the moment it is read, which is why a 19-digit id often comes back off by one. The report flags any literal that would round, so you can see it before your consumers do.// and /* */ comments, unquoted keys, single and curly quotes, NaN, Infinity, -Infinity, undefined, Python's True, False and None, hex numbers, leading zeros, leading plus signs, a missing digit either side of a decimal point, unknown backslash escapes, and raw control characters inside strings. It stops short of anything ambiguous: it will not close a string for you or invent a missing bracket.