JSON Beautifier

One editing surface instead of an input box and an output box. Paste and the document reformats where it sits, and when it will not parse, the bar underneath names the line, the column and the reason.

JSON formatting and validation workspace

Indent

Paste JSON above. It is checked while you type.

Lines 0Size 0 BDepth 0Keys 0
Try it:

Most JSON arrives broken, so the error message matters more than the indentation

Indenting valid JSON is a solved problem. Every language ships it in one line. What sends people to a formatter is a 4,000 character response on a single line that a parser has rejected, with a message like Unexpected token } in JSON at position 2847 and no way to work out where position 2847 sits.

That is the job this page is built around. Paste the document and it reformats in place, so there is no second box to copy back out of. Underneath, a bar reports one of three things: the document is fine, the document is fine but has a repeated key, or the document is broken at line 42 column 17 because a comma follows the last property.

Why one editing surface rather than two panes

The two pane layout comes from diff tools, where you genuinely need to see two versions. Formatting has one version. Splitting it in half gives each side a narrow column, forces a copy step at the end, and leaves you editing on the left while reading the result on the right.

Here the document reformats where it sits. Fix the flagged line, watch the bar go green, press Copy. The full width also matters more than it sounds, since deeply nested API responses run wide at 8 space indentation and horizontal scrolling is the fastest way to lose your place in a structure.

What the checker names, and what a browser tells you instead

The page parses JSON with its own scanner rather than handing the text to JSON.parse. Built-in parsers stop at the first problem and describe it in terms of the grammar, which is accurate and close to useless when you are looking at somebody else's export. A scanner that knows the mistakes people actually make is able to name the cause.

The same broken document, two descriptions
What is wrongWhat a browser reportsWhat this page reports
{'id': 1}Unexpected token ' at position 1Property names must use double quotes
{"a": 1,}Unexpected token } at position 8Trailing comma before the closing brace
{"a": 1 "b": 2}Unexpected string at position 8Expected a comma or a closing brace after this value
{"ok": True}Unexpected token T at position 7Python capitalises its booleans, JSON does not
{"n": 007}Unexpected number at position 7Numbers cannot carry a leading zero
{"p": "C:\Users"}Bad escaped character at position 10Unknown escape sequence, a Windows path needs each backslash doubled

Every message comes with a line and column, the offending line printed with a marker under the exact character, and a button that drops your cursor there.

Position numbers are the part that wastes the most time. A character offset is meaningless in a minified document, and it shifts the moment you edit anything above it. Line and column survive editing and match what your editor shows you when the same file fails in a build step.

Repeated keys are valid JSON and still a bug

An object holding the same key twice parses without complaint in every language. RFC 8259 calls the behaviour undefined, so implementations pick their own answer. JavaScript and Python keep the last one. Some Go and Java setups keep the first. A few parsers raise an error.

That disagreement is a genuine source of production bugs. A config file with "timeout" set twice behaves one way in the service that reads it with Jackson and another way in the deploy script that reads it with Python, and neither logs anything unusual.

The bar turns amber and names the repeated key when it finds one. Formatting keeps the last value, which matches what JavaScript and Python do, so the output is what most readers were already seeing.

Long numbers survive the round trip

This is the failure people find hardest to believe. Run a Twitter or Discord snowflake ID through most browser based formatters and the digits change:

{"id": 1234567890123456789} becomes {"id": 1234567890123456800}

Nothing went wrong with the formatter. JSON.parse turns every number into a JavaScript double, doubles carry about 15 digits of precision, and 19 digit IDs do not fit. The value is already wrong before any formatting happens, and a formatter built on parse then stringify hands you the damaged version back.

The scanner here keeps the original digits of every number as text and writes them back untouched. Long IDs, high precision decimals, 1E+5, -0.0: all of them come out the way they went in. Numbers are the one place where a formatter is able to quietly corrupt data, so it is worth checking whichever tool you use with a 19 digit integer.

Choosing an indent, and why sorting keys is a decision

Two spaces is the default here because it is what prettier, npm and most JavaScript projects emit, and because deep nesting stays on screen. Four suits Python and C# codebases. Tabs are the right answer when the file lands in a repository where a reader controls their own width. Eight exists for the rare structure that is easier to read when the levels are unmistakable.

Sorting keys alphabetically is off by default, and it deserves more thought than a formatting toggle usually gets. Sorting makes two API responses diffable, since a field that moved position no longer shows as a change. It also destroys meaningful ordering. Configuration files often put name and version first on purpose, and an alphabetical rewrite buries them. Sort when you are comparing, leave it alone when you are committing.

The tree is for finding paths, not for reading

The Tree tab renders the parsed document as collapsible rows with a count against every object and array. Two levels open by default and deeper branches build only when expanded, so a 5 MB response does not stall the tab.

Its real use is the path. Click any row and the dotted path lands on your clipboard, ready for a jq filter, a JSONPath expression or a line of application code. Keys that are not plain identifiers come out bracketed and quoted, so $.users[0]["content-type"] pastes in correctly rather than breaking at the hyphen.

What runs where

Everything happens in the page. There is no upload, no API call and no request in the formatting path, which is the only sane arrangement given what people paste into JSON tools. API responses carry session tokens, config files carry connection strings, and a support export carries other people's personal data. None of it should cross a network to gain some whitespace.

The practical consequence is that the browser tab is the limit. Documents up to a few megabytes format instantly. Past 300 KB the syntax colouring turns itself off, since keeping an overlay in step with every keystroke on a large document is what makes these pages stutter. Validation, formatting and the tree keep working.

Where this tool stops

JSON formatting questions that come up mid task

The parts of parsing and formatting that catch people out.

Why does my JSON fail here when JavaScript accepts it?

Because a JavaScript object literal is not JSON. Single quoted strings, unquoted keys, trailing commas, comments and undefined are all fine in a .js file and all invalid JSON. The two look alike, which is exactly why the confusion is so common. If the text came out of a code editor rather than a network response, that is almost always the cause.

What is a trailing comma and why does it break everything?

It is the comma after the last item, as in {"a": 1,}. JavaScript and Python both allow one so that adding a line later touches a single line in a diff. JSON forbids it outright. Since most JSON is written by hand in an editor that permits it, this is the single most common error people hit, and it is why the message names it directly instead of reporting an unexpected token.

My long ID numbers change value after formatting. What happened?

A tool built on JSON.parse converted them to JavaScript doubles, which carry roughly 15 digits of precision. A 19 digit snowflake ID does not fit and gets rounded, so 1234567890123456789 comes back as 1234567890123456800. This page keeps the digits of every number as text and writes them back unchanged. Anywhere else, send long IDs as strings.

Is my data sent to a server?

No. Parsing, formatting and the tree all run in the page, and there is no network request involved in any of it. Turn off your connection after the page loads and everything still works. That matters because JSON pasted into a formatter routinely contains auth tokens, connection strings and customer records.

Should I use two spaces, four spaces or tabs?

Match whatever the file lives beside. JavaScript and web tooling settled on two, Python and C# projects lean towards four, and tabs are the better answer for a file in a shared repository because each reader controls the width. For a file only a machine reads, minify it and skip the question. Indentation is presentation, so no parser cares either way.

What does sorting keys actually change?

Only the order they are written in. JSON objects are unordered by specification, so no parser treats a sorted document differently. Sorting is useful when you are diffing two API responses and want a moved field to stop showing as a change. It hurts when a config file deliberately leads with name and version, since alphabetical order buries them halfway down.

Why is a duplicate key flagged when the document is valid?

Because valid and safe are different things. RFC 8259 permits the same key twice and leaves the outcome undefined, so JavaScript and Python keep the last value while some Go and Java parsers keep the first. Two services reading the same config file can therefore disagree without either one logging an error. Formatting here keeps the last value.

Can it handle a file with comments in it?

No, and that is deliberate. A file with comments is JSONC or JSON5, not JSON. Accepting it here would mean handing back something that fails in the next parser it meets. The checker names the comment rather than reporting an unexpected character, so at least the fix is obvious.

How large a document will this handle?

A few megabytes format without a pause on a desktop browser. Past 300 KB the syntax colouring switches off, since the overlay redraw is what makes large documents feel sluggish while typing. Beyond about 20 MB, use jq or a streaming parser instead, because the whole document sits in tab memory.