About this JSON tool

A JSON formatter, validator and tree viewer with no server behind it, written because the popular hosted alternatives turned out to have been quietly keeping copies of what people pasted into them.

Why it exists

There is no shortage of online JSON formatters. There was, however, a very good reason to build another one.

In November 2025 the security firm watchTowr Labs published research on two of the most-visited online code-formatting utilities. Their “save” and “share” features stored submissions server-side behind predictable, enumerable links. Researchers walked the archive and recovered more than 80,000 files, over 5 GB, accumulated across roughly five years: Active Directory credentials, cloud access keys, database connection strings, private keys, and personal data belonging to customers of banks, government bodies and healthcare providers. Fake credentials planted as canaries were exploited within days.

Nothing was hacked. The data was stored, given a guessable address, and left there.

Formatting JSON has not needed a server since browsers gained a native JSON.parse in 2011. A page that uploads your document in order to add whitespace to it has taken custody of your data for no functional reason whatsoever. This tool does not. The full story, with links to the reporting, is on the main page.

What it does

  • Format with two-space, four-space or tab indent.
  • Validate, reporting the first error with its line, column, character offset, the offending line and a caret beneath the exact character.
  • Minify, with the byte count before and after.
  • Explore, as a keyboard-operable collapsible tree that shows child counts on unopened nodes and searches keys and values by substring.
  • Measure: UTF-8 bytes in and out, total keys, maximum nesting depth, total values, and the object and array counts.
  • Copy to the clipboard, or download as a .json file built locally in the page.

How the error locator works

Validity is decided by the browser’s own JSON.parse, so the verdict is exactly what your application will produce. But JSON.parse messages are terse and engine-specific: Chrome, Firefox and Safari word the same failure three different ways, and Safari frequently gives no position at all.

So when JSON.parse refuses, a second pass runs — a strict RFC 8259 scanner written for this tool. It walks the text as an explicit state machine with its own stack, rather than as a recursive function, which is why a document nested ten thousand levels deep produces a diagnosis instead of a stack overflow. It stops at the first illegal construct and tries to name it in developers’ terms rather than the parser’s:

  • trailing commas, distinguishing object from array;
  • single-quoted strings and unquoted keys — the JavaScript-object-literal mistakes;
  • NaN, Infinity, undefined, and the Python spellings None, True, False;
  • invalid escapes, with a specific note for the Windows-path case where \U fails loudly and \n fails silently;
  • unescaped control characters inside strings;
  • numbers with leading zeros, leading dots, trailing dots, plus signs, hex or digit separators;
  • a byte order mark, curly quotes, non-breaking spaces, zero-width spaces and other invisible characters, named by code point;
  • comments, HTML error pages, concatenated documents and NDJSON;
  • truncation, reported with the number of brackets still open.

The scanner is cross-checked against JSON.parse on twenty thousand randomly mutated documents. On every one, the two agree about whether the input is valid — so the explanation can never contradict the verdict. If a future engine ever rejects something the scanner accepts, the tool falls back to reporting the engine’s own message and converting its position to a line and column.

How the tree stays fast

People paste multi-megabyte payloads into formatters. A tree that eagerly builds a DOM node for every value in a 6 MB document will create hundreds of thousands of elements and lock the tab for several seconds.

This one does not build a node until you ask for it. On load it opens roughly three hundred rows breadth-first and then stops. Any container with more than two hundred children renders them in pages, with a row at the bottom that loads the next block. Collapsed nodes show their size — {…} 12 keys, […] 340 items — so a subtree can be judged without being opened. Above about 300 KB of input, formatting stops running on every keystroke and waits for a button press, and the text output pane renders only the first 250,000 characters while Copy and Download continue to use the whole document.

The result is that the constraint is your machine’s memory rather than any arbitrary cap in the page, and that nothing you do makes the tab stop responding.

The escaping rule

Every fragment of your document that reaches the page is written with textContent or as a text node. Nothing is ever assembled into an HTML string and assigned to innerHTML. Paste a key or a value containing <img src=x onerror=alert(1)> and it renders as those literal characters in the tree, in the output pane, in the search results and in any error message.

The page also never calls eval or the Function constructor. A JSON tool that evaluated its input would let a pasted payload run code in your session rather than merely be read — the single worst bug a tool like this could have, and the reason parsing goes through JSON.parse and nothing else.

What it deliberately does not do

  • Repair. It tells you exactly what is wrong and where. It will not guess where a missing brace belongs, because a plausible guess that is wrong gets committed with confidence.
  • Accept JSON5, JSONC, HJSON or YAML. Comments, trailing commas, unquoted keys and single quotes are reported as errors. A lenient parser would let you ship a file your production runtime rejects.
  • Validate against a schema. Syntax only. JSON Schema is a different job, well served by Ajv and its equivalents.
  • Store or share. No accounts, no saved snippets, no permalinks. That is not a missing feature; it is the entire point.

Known limitations

Numbers pass through JavaScript’s IEEE 754 doubles, so integers beyond 253 − 1 lose precision and decimals round the way they do everywhere else in JavaScript. Duplicate keys collapse to the last occurrence, matching JSON.parse. Integer-like keys are reordered ahead of string keys, which is a rule of the JavaScript object model rather than a choice made here. Lone surrogates in \u escapes are accepted, again matching JSON.parse, even though they cannot be encoded as valid UTF-8. Documents beyond a few hundred megabytes belong in a streaming parser such as jq or ijson, not in any browser page.

Privacy and cost

Everything runs client-side. The document you paste is never transmitted, logged or stored — there is no upload endpoint on this page and no code capable of using one. There is no account and nothing to install. Once the page has loaded, nothing further is fetched, so you can save it to disk and use it with the network switched off.

The site is free to use. It may carry advertising to cover hosting; see the privacy policy for what that would mean. An advertising script is third-party code that makes its own requests, but it cannot see the contents of a textarea it did not create, so “what you paste never leaves your device” is a claim that survives it.

Corrections

If this tool reports a document as valid that your parser rejects, or reports an error at the wrong position, that is a bug worth hearing about. Please include the exact input and what you expected. The contact page has the address.