How Format JSON is built
Format JSON pretty-prints, minifies, or validates JSON, entirely client-side. The formatting and minifying are one line of native JavaScript each; the interesting engineering problem turned out to be something smaller and more specific — telling you where your JSON is broken, when the three major browser engines don’t even agree on whether they’ll tell you that at all.
Tech used
Formatting is JSON.stringify, twice
Pretty-print, minify, and validate are treated as one operation here, not three separate features — all of them start from JSON.parse(input), and the only thing that changes is what’s done with the result: JSON.stringify(parsed, null, indent) for pretty-print (with a selectable 2-space, 4-space, or tab indent), JSON.stringify(parsed) with no third argument for minify, and just checking that JSON.parse didn’t throw for validate. No library is involved anywhere in this path.
Where three browsers disagree
When JSON.parse throws, the resulting error message is the one place this gets genuinely browser-specific, and it hasn’t been standardized. The tool’s parser (locateParseError) was built by actually running around a dozen malformed-JSON inputs through JSON.parse in real Chromium, Firefox, and WebKit and reading what came back, rather than assuming Node’s own error format applies everywhere:
- Firefox consistently embeds the location as
at line L column C of the JSON data— always parseable, always trustworthy. - Chromium sometimes embeds
at position N (line L column C), but not always — a trailing comma before a closing], for instance, produces only a quoted snippet of the offending text with no position information at all. - WebKit never exposes a location. Every error is a bare message, full stop.
The parser handles this by trying the most specific case first — an explicit line L column C in the message, used directly — then falling back to a raw character position N, converted into a line and column by counting characters and newlines up to that offset in the original input. When neither pattern is present, it shows the engine’s raw message with no fabricated location, rather than guessing at a line number the browser never actually gave it. All three paths are covered by tests built directly against the real captured message strings from each engine, and the whole thing runs in the e2e suite across all four browser projects — including WebKit, where the graceful “no location, just the message” fallback is the one that actually fires.
Byte size means bytes, not .length
The before/after size comparison shown for a minify operation uses TextEncoder to get an actual UTF-8 byte count, not JavaScript’s string .length — which counts UTF-16 code units and undercounts anything outside the Basic Multilingual Plane, or misrepresents multi-byte UTF-8 characters generally. For JSON containing non-ASCII strings, the two numbers can differ meaningfully, and the byte count is the one that matches what you’d actually see in a saved file’s size on disk.
Implementation & operational notes
Validate mode has no output artifact. Format and minify produce a transformed result worth copying or downloading; validate only produces a yes/no plus, on failure, the error location described above — so it shows a status banner instead of an empty output box with nothing to copy.
Both file upload and pasted text are accepted, using the same file-validation pattern as the rest of this catalog — a machine-readable validation result rather than a hardcoded message string, so the UI can localize it per the active language.
Try it / source
- Tool: Format JSON
- Source: github.com/GeppettoAndRomero/format-json