JSON Formatter
Paste or drop any JSON and it comes back consistently indented. Nothing you paste is uploaded, and a syntax error gets an exact line and column — even in a 100 MB file.
Ctrl / Cmd + Enter formats as well.
Click any { } or [ ] to collapse or expand it. The number beside a bracket is its item count, so an empty array is obvious at a glance. Rendering stops at 5,000 nodes.
Paste JSON into the Format & validate tab first.
Supported: $, .key, ..key (recursive descent), [0] and [*]. That is the subset developers actually use, not the whole JSONPath grammar.
Compared structurally, not as text. Indentation, key order and 1.0 vs 1 are not reported as differences.
The data comes from the input box on the Format & validate tab. Supported: type, required, properties, items, enum, const, numeric bounds, string length and pattern, array length and uniqueness, and additionalProperties. Not supported yet: $ref, allOf/anyOf/oneOf and format.
A JSON formatter takes compressed or hand-indented JSON and rewrites it with consistent indentation and line breaks, without changing the data. The same operation is also called a JSON beautifier, a pretty printer, a prettifier and an indenter — five names, one operation, and they are interchangeable in every tool you will find.
What separates the tools in this category is not the indentation. It is two other things: whether the page survives a large file, and whether a syntax error tells you where it is. This one runs the work in a Web Worker, so the tab keeps responding while a 100 MB file is in flight, and it carries its own parser so the error message names a line and a column instead of quoting a fragment of your text back at you.
Nothing is uploaded. There is no upload step to disable: the file is read with FileReader, handed to a worker on the same origin, and the result is written into the page. If you want to check, open the Network panel and format something: besides the ad tag this page loads, the only entries are this page's own five files, and no request carries your document.
How fast, in numbers
Every number below was measured on this build, in Chrome 152 / Windows 11, by loading
a generated file in the page and timing the worker round-trip. Reproduce it with
bench.html in the repository — the code that produced these figures
ships with the site.
| Input | Format to 2-space indent | Notes |
|---|---|---|
| 1 MB · 3,758 records | 43 ms | includes worker startup, which is most of the cost at this size |
| 10 MB · 37,583 records | 197 ms | 1,014,743 output lines, 826,827 nodes counted on the way out |
| 50 MB · 187,916 records | 759 ms | 49.9 MB in, 5,073,734 lines out |
| 100 MB · 375,833 records | 1496 ms | 10,147,493 lines out; 721 MB peak JS heap |
| 100 MB, minified, same file | 926 ms | minify direction is cheaper than pretty-printing |
100 MB parsed with JSON.parse + JSON.stringify only |
964 ms | the floor: JSON.parse + JSON.stringify(x,null,2) with no stats pass |
And the part that actually decides whether a tool feels broken: the main thread stays free while the worker crunches. Formatting a 20 MB file in the worker, the page kept rendering at 53.5–63.7 fps (worst frame to average) with a worst single-frame gap of 18.7 ms. Doing the same parse on the main thread instead blocks it for 225 ms, of which roughly 30 ms is a wait the harness inserts on purpose so the freeze is easy to see — which is the entire difference between "the site is fast" and "the site froze, I clicked twice, it's dead."
Where the extra time goes. We are 1.55× slower end to end than calling JSON.parse and JSON.stringify directly. That ratio includes posting a 100 MB string into the worker and taking the result back out, not only the extra tree walk that reports to report node count, nesting depth and byte totals — the numbers
under the output box. If you only want re-indented text and don't care what it says
about the shape, native code in your own terminal is faster. We'd rather be honest
about that than quote a number we didn't pay for.
What happens when the JSON is broken
Two formats of the same failure are handled differently, and the difference matters on big files.
When a browser's parser can tell you where, it says so. When it can't — and modern
V8 often can't; a message like
Unexpected token '@', … is not valid JSON carries a
snippet instead of a position — a formatter that only relays the engine leaves you
counting commas in a 40,000-line file.
So this page ships its own scanner. It only runs on the failure path: a valid file never pays for it. On an invalid file it walks the text once, records the first position where the grammar can't continue, and converts that to line and column.
| File | Error at | Line/column reported | Time |
|---|---|---|---|
| 8 MB, pretty-printed | line 385,230 | exact match | 163 ms |
| 100 MB, pretty-printed | line 2,419,794 | exact match | 1026 ms |
| 100 MB, minified (single line) | last character of the file | exact match | 1613 ms |
Eighteen injected faults across six file sizes, error placed at 2%, 50% and 99.5% of the document: all eighteen returned the exact expected line and column. The cap is 192 MiB of text, counted in characters, so a file full of non-ASCII hits it sooner. Past that we stop scanning and hand you the engine's raw message, because the scan costs about a second per hundred megabytes and a multi-second wait for a line number is a bad trade even when it is accurate.
The five mistakes that cause most of them
JSON.parse accepts a narrow grammar. These are the failures it reports
most often, what the position points at, and what the fix actually is:
| What you wrote | Why it is not JSON | Where the error lands |
|---|---|---|
{"a": 1,} | Trailing comma | on the } |
{'a': 1} | Single quotes are not string delimiters | on the ' |
{a: 1} | Keys must be quoted strings | on a |
[1, 2,] | Trailing comma in an array | on the ] |
{"a": undefined} | undefined is not a JSON value |
on u |
// comment | JSON has no comments | on the first / |
{
"items": [1, 2, 3,],
'name': "x",
// count
"n": undefined
}
The last two rows are the ones that make people distrust the tool: a file copied out
of .eslintrc.json, tsconfig.json or a VS Code settings file looks
perfectly fine in the editor and is rejected here. It is not a bug. Those are
JSONC — JSON with comments — and comments, trailing commas and single
quotes are extensions those editors parse, not JSON. If the file must be real JSON,
delete them; if it only has to work in that editor, leave them.
What the format actually allows
JSON has two specifications, and they cover different ground:
- RFC 8259 — from IETF. It defines JSON as a data interchange format, including the requirement that text exchanged between systems be UTF-8. This is the one engineering teams comply with.
- ECMA-404 — from Ecma International. It describes the grammar only, and says nothing about encoding or transmission.
Neither one allows comments, trailing commas, single-quoted strings, unquoted keys,
NaN, Infinity, undefined, or a byte-order mark at
the start of a file. There is no number type in the grammar beyond "number": no
integers, no floats, no size limits. That is why everything in the next section can
happen.
What formatting can quietly do to your data
A formatter is a decoder followed by an encoder, and that round trip is not free.
Six things can change even though the file still parses. All six are pinned by assertions in the harness that ships with this site (test/index.html, run in Chrome 152 / Windows 11):
- Integers outside ±(2^53 − 1) lose digits.
9007199254740993becomes9007199254740992. JavaScript has no 64-bit integer type inJSON.parse, so snowflake IDs, Twitter/X IDs and nanosecond timestamps will be silently rewritten. If your JSON came from a service that puts IDs in numbers, that is a bug in the service — but the formatter is where you lose the evidence. - Big numbers get exponent notation.
1e21comes back as1e+21, which is valid JSON but not what most databases accept when you paste it back. - Duplicate keys collapse.
{"a":1,"a":2}parses without error and keeps the last value. The first one disappears with no warning. Keeping the last value is what JavaScript parsers do; RFC 8259 only says names SHOULD be unique and leaves the behaviour unspecified, so another decoder can disagree with you. -0becomes0. Harmless in JSON, but if the value is a sign bit in a protocol, it is no longer the same bytes.- A number too big to represent becomes
null.{"n":1e999}comes back as{"n":null}— not an error, not an approximation, andnullis a perfectly legal value, so nothing downstream complains. - Integer-looking keys move to the front.
{"b":1,"2":2}is stored by JavaScript as{"2":2,"b":1}, so the output is reordered before any sorting option is involved. If anything hashes or signs over key order, this bites whether or not you tick a thing.
NaN and Infinity are not JSON at all, so a formatter will
reject them — and if you got such a file from JavaScript, note that
JSON.stringify already turned them into null before you ever
saved it. Keys whose value is undefined are dropped entirely.
Practical rule: if exact numeric fidelity matters, quote the numbers as strings on the way out of your API. Everything else in this list is then moot.
Two spaces, four spaces, or a tab
Whitespace carries no meaning in JSON — both grammars ignore it entirely — so this is purely convention, and the convention has already settled:
- 2 spaces is the default everywhere the default is written down:
JSON.stringify(obj, null, 2)in tutorials, and Prettier'stabWidth. - 4 spaces shows up in Python shops, because PEP 8 asks for four and
json.dump(obj, f, indent=4)follows it. - Tab is common in Go, where
gofmtindents with tabs andjson.MarshalIndent(v, "", "\t")mirrors that.
Picking either is fine. Mixing them is the actual problem: a repository where some
.json files use 2 spaces and others use 4 turns every edit into a
file-wide reformat, and the diff noise hides the change you came to review.
Sorting keys alphabetically is the other option with a trap in it. It is safe for objects — key order is not part of JSON's meaning — and this page never touches array order, because for an array, order is the data.
Formatting it without a browser
If the file is already on your machine and there is a terminal nearby, you do not need this page:
# Python, no arguments to remember python3 -m json.tool in.json # jq, if it is installed jq . in.json
// or six lines of your own
const fs = require("fs");
const raw = fs.readFileSync(process.argv[2], "utf8");
fs.writeFileSync(process.argv[3],
JSON.stringify(JSON.parse(raw), null, 2));
So where does a web page win? Three places. You have the JSON in a clipboard and no
file. You want to look at the structure rather than read it, which is what the tree
view is for. And the file is big enough that cating it into a terminal is
unusable — which is exactly why the worker and the error-position logic exist.
One thing a local python -m json.tool gives you that no browser page
can: an exit code. If you are deciding whether to format in CI, use the CLI.
Frequently asked questions
Does my JSON get uploaded anywhere?
No. The file is read with FileReader and processed in a Web Worker on the same origin — nothing your document contains reaches a server. Open the Network panel while formatting: besides the ad tag, you will see only this page's own assets — no request this page makes carries your document. There is also nothing to leak afterwards: no save button, no share link, no recent list — no URL anywhere points at what you pasted.
How big a file can it handle?
There is no cap on how big a file you can paste. The caps that do exist are on what we draw and on how far we hunt for an error, and both are listed here. Measured on Chrome 152 / Windows 11: a 99.9 MB file with 375,833 records formats in 1496 ms, and locating a syntax error in that same size takes 1026 ms. The real limits are your available memory and the browser's maximum string length — measured 7.2× at 100 MB — 721 MB of JS heap sampled right after the run, via a Chrome-only API — because the text, the parsed object and the output all exist at once. Budget eight to ten times to be safe.
What is the difference between a formatter and a validator?
A validator answers one question — is this legal JSON. A formatter answers another — show it to me readably. They are usually the same code, because you cannot format something you have not parsed, so this page reports syntax errors while formatting and does not need a separate validation step.
Why does it say my JSON is invalid when it works fine elsewhere?
Almost always one of four things: comments, trailing commas, single-quoted strings, or unquoted keys. Comments and trailing commas are what JSONC allows; single-quoted strings and unquoted keys are JSON5 only, and a jsonc parser such as VS Code's rejects those two. All four are illegal in JSON. Files from VS Code settings, tsconfig.json, .eslintrc.json and devcontainer.json are JSONC.
Will formatting change my values?
Not the structure, and not strings. It can change numbers: integers outside ±(2^53 − 1) are not guaranteed to survive (9007199254740993 becomes 9007199254740992), large magnitudes switch to 1e+21 notation, and duplicate keys collapse to the last one. If exact numeric fidelity matters, transport those numbers as strings.
Can it sort keys alphabetically?
Yes — the Sort keys option recurses through every object at every level. Array element order is never changed, because array order is meaningful and reordering it would corrupt the data.
Can I use a tab, or a specific indent width?
2, 3 and 4 spaces and a tab are all offered. The output is identical apart from the whitespace, so choose whatever your repository already agreed on.
Why does this page load so fast?
Because it is one HTML file plus four assets, all same-origin, with no web-font requests of any kind. When advertising is on, Google's scripts are the only cross-origin traffic here — that is what pays for the page, and the tool code adds nothing to it. The processing code is one file shared between the page and the worker rather than being duplicated. That is the whole trick — there is nothing clever about the formatting itself.
How to format JSON
- Get the JSON in: Paste it into the input box, drop a .json file on the page, or click to choose one. Nothing leaves the browser.
- Pick the indentation: 2 spaces is the common default. Choose 4 or tab if your project already uses one — output differs only in whitespace.
- Read the error, if there is one: An invalid file gets the exact line and column of the first place the grammar breaks, plus the surrounding text. Trailing commas and comments are the usual culprits.
- Take the result back out: Copy to clipboard or download the formatted file. Sorting keys is optional and never touches array order.