JSON Tree Viewer
Navigate huge JSON as a collapsible, searchable tree — copy any value or path, entirely on this device.
Paste JSON, drop a file, or Load sample
Press ? for shortcuts
Do it offline:$ jq . response.json | less
How to use it
- Paste JSON, drop a .json file, or load the sample. Small inputs parse as you type; past 2 MB the button carries the size — Parse 8.4 MB — and nothing heavy starts until you press it (or ⌘/Ctrl + Enter).
- Walk the tree with the arrow keys: ↓ and ↑ move between rows, → opens a container, ← closes one or climbs to its parent, and typing letters jumps to the next key with that prefix. The level selector folds the document to depth 1, 2 or 3 in one action.
- Search keys and values together or separately, as literal text or a regular expression. The readout counts matching nodes; Enter and Shift + Enter step through them, opening only the branches needed to show each hit.
- Select a row and the breadcrumb offers Copy value — the node's original characters — and Copy path in the syntax you need. Share folds the document and your open branches into a link; Download saves the bytes as .json.
Drop an API response into this page and read it as a tree instead of a wall of brackets: collapse whatever does not matter, search every key and value at once, and copy the exact path — JSONPath, jq, or JavaScript notation — to the node that does. Files up to 50 MB parse in a background thread, entirely on this device, and the pasted text itself is never rewritten.
How it works
RFC 8259 defines a JSON document as one value built from six shapes: objects, arrays, strings, numbers, booleans and null. Those six are the entire grammar — everything else is nesting — and they are exactly the six kind badges on the rows here: obj, arr, str, num, bool, null. Nesting is also what defeats eyes in flat text, usually by the third level of braces. A tree restores the shape the grammar had in mind, with each container's true child count on its row before you commit to opening it.
Opening a file here builds no JavaScript object at all. One pass walks the text and records, for every value, its kind, its depth, where its subtree ends, how many direct children it has, and the exact span of source text it occupies. That flat preorder table is the entire data structure: collapsing a node is a constant-time jump to its subtree's end, and search runs over the table rather than over pixels. Past a quarter megabyte the pass moves to a worker thread, and the finished table is handed back without a copy.
Every row knows its own address, spoken in four dialects because four ecosystems disagree on the spelling. JSONPath — standardized as RFC 9535 — writes $.items[3].price.currency, the $ standing for the document root, and is what API tooling and JSON databases expect. jq drops the $: .items[3].price.currency is a runnable filter exactly as printed. JavaScript wants dot notation until a key carries a space, a dash or a dot of its own, at which point only the bracket spelling is legal — so the dot syntax here switches to bracket segments for such keys automatically, and what you copy always evaluates.
What reaches your screen is the original text, sliced. A number row shows the digits that were in the file, not the result of a trip through a 64-bit float, and Copy value copies that same slice — which is why identifiers beyond 2⁵³ keep their last digit here. The table knows every row's position in advance, so the page renders only the rows in view, swapping them as you scroll. That is the whole trick behind opening 50 MB without the tab freezing: the DOM never holds the document, just your window into it, and strings preview at 200 characters until asked to unfold.
Examples
{
"order": "A-1042",
"items": [
{ "sku": "KB-701", "qty": 1 },
{ "sku": "MS-220", "qty": 2 },
{ "sku": "HD-119", "qty": 1 },
{ "sku": "DK-500", "qty": 1, "price": { "amount": "129.00", "currency": "EUR" } }
]
}JSONPath $.items[3].price.currency
jq .items[3].price.currencySelect the currency row and this is Copy path in its first two syntaxes: the JSONPath form for API test suites and the jq filter, ready for a terminal. The JavaScript dot and bracket spellings sit in the same menu, and keys that are not valid identifiers fall back to bracket segments on their own.
{"invoice":"F-2209","id":9223372036854775807}9223372036854775807Copy value on the id row, verbatim — that is int64 max, a real database ceiling. The viewer never converts the number to a JavaScript float; feed the same document to JSON.parse and it answers 9223372036854776000 — 193 too many, with no warning anywhere.
{
"events": [
{ "type": "refund.created", "amount": 1250 },
{ "type": "charge.captured", "amount": 9900 },
{ "type": "refund.updated", "amount": 1250 }
],
"refund_policy": "manual-review"
}3 matchesThe search readout for refund across this feed — two event types matched by value, one policy field matched by key name. Enter steps forward through the hits, Shift + Enter back, and each jump opens exactly the ancestors its row needs.
{
"id": "usr_01",
"id": "usr_02"
}duplicate key `id` at lines 2 and 3 — most parsers keep the lastThe warning raised while both rows stay visible in the tree, each wearing a badge. JSON.parse hands you usr_02 and never mentions that usr_01 existed — seeing both is how you learn which half of a merge wrote the value that won.
Every example on this page is executed against the same engine that powers the tool, on every build.
Gotchas worth knowing
Arrays with a million elements page in blocks of 1,000
A giant array is opened, not dumped: its first 1,000 children render with a showing 1–1,000 · load more row at the bottom of the block, while the array's badge states the true total. Paging happens inside the scroller, so a match or keyboard jump deep in the array brings its page along.
A 2 MB string in one field will not flood the screen
String rows preview their first 200 characters. Longer ones — past roughly 10 KB — take an expand-in-place affordance that unfolds a 64 KB window of the text and says so plainly. Copy value ignores the windowing and carries the complete string as written in the source.
Duplicate keys are shown, not resolved
When an object declares the same key twice, both rows appear, each flagged, and a warning names the key and both line numbers. After twenty such warnings the list stops and says so. On inputs past 5 MB the duplicate check is skipped — and declared skipped — rather than silently slowing the parse down.
64-bit identifiers keep every digit
Snowflake ids, database sequence values and money-in-cents counts exceed 2⁵³, the point where JavaScript's number type starts rounding to even. Rows and copies are slices of the source text, never parsed floats, so the digits you see are the digits that arrived. The second example above is executable proof.
Data URIs are labeled, not rendered
A string that starts with data: earns a chip naming the payload's size instead of a 300,000-character preview. The viewer does not decode or display the payload — Copy value still hands the complete URI to whatever tool comes next.
Depth is bounded at 100,000 levels — iteratively
The parser tracks nesting with an explicit stack, so a pathological single chain of arrays thousands of levels deep parses where recursive parsers blow their call stack. Past the documented 100,000-level cap it stops with a named error; real documents rarely pass depth 20, and the status line prints yours.
Past 50 MB the page refuses the whole file
Under the cap, parsing, search and scrolling stay responsive; over it, the banner names the limit and declines to work on a truncated slice whose line numbers would all be lies. At that size the terminal serves you better: jq . file.json | less streams anything, with the same privacy.
Common errors
Unexpected '}' at line 4, column 1 — probable cause: trailing comma after the last property.- The position points at the brace the stray comma made illegal, not at the comma itself — JSON5 config files allow the style; JSON does not. What is particular to this viewer: the last good tree stays on screen while you repair the input, dimmed and marked stale, so your open branches are not thrown away.
Input is larger than the 50 MB limit for this tool. For bigger payloads use the CLI: jq . file.json | less- The refusal is total by design: viewing only the first 50 MB of a 70 MB file would mean every count, path and line number described a document that does not exist. Nothing you pasted is altered or lost. The suggested command runs on your machine, streams any size, and is exactly as private as this page.
The search pattern is not a valid regular expression. Invalid regular expression: /(refund/: Unterminated group- The first line is this page talking; the second is the JavaScript engine's own complaint, preserved word for word — the text teammates and search results recognize. An unclosed group or bracket is the usual culprit. Until the pattern compiles nothing is searched — and if you meant the parentheses literally, switch regex off and they become plain text.
FAQ
What is the fastest way to understand a JSON file I have never seen?
Collapse it first. Fold the document to level 2 and it becomes a table of contents: every top-level section with a true child count on each container. Open only the branch you care about — or search for a field name you expect and let next and previous walk you through every place it occurs.
JSONPath, jq, dot or bracket — which path should I copy?
Match the destination. JSONPath, with its leading $, belongs in API test suites, JSON databases and OpenAPI tooling. The jq form is a command-line filter you can run unedited. Dot notation drops into JavaScript and most dynamic languages, and bracket notation is the same address spelled so that keys holding spaces, dashes or dots cannot break it. All four name the same node.
Why is there no way to edit a value in the tree?
Because a viewer and an editor make opposite promises. This page guarantees that what you see is exactly what arrived; the moment editing exists, that guarantee needs change tracking, undo history and re-serialization rules, and reading gets heavier for everyone. Tree editing is a different tool's job; the read-only contract is what lets 50 MB feel weightless here.
Does my JSON leave this device — and how large can it go?
It never leaves. Parsing, search and path building run in your browser — bigger inputs in a worker thread — and dropped files are read locally, with zero network requests. The working limit is 50 MB of text; from about 2 MB the page waits for an explicit go-ahead rather than re-parsing every keystroke.
My document has the same key twice — which value wins?
Whichever tool reads it decides — parsers genuinely disagree, a trap the JSON Validator page walks through in full. This viewer's contribution is forensics: it refuses to vote, shows every occurrence with a badge, and names both lines. The real danger is not either value; it is not knowing the second one existed.
Why does this page show my number exactly when the console rounds it?
The console parses; this page quotes. Any tool that funnels JSON through IEEE 754 doubles rounds integers past 2⁵³, which is how two systems end up disagreeing about an id. The tree skips that conversion for display — each row is a slice of your original text, and Copy value copies the same slice, so the digits survive untouched.
Can I send a coworker a link that opens on one specific node?
Yes. Share encodes the document, your open branches and the highlighted node into the link's #fragment — the part of a URL a browser never transmits to any server, including this one. It reopens with the same node expanded and lit. Two honest limits: past a compressed-size cap the page offers a state file to download instead of a link, and input shaped like a credential prompts for confirmation first.
Keyboard shortcuts
- ⌘/Ctrl + Enter
- Parse now at any size — the large-input band's Run shortcut; works with the cursor still in the textarea.
- ⌘/Ctrl + S
- Download the document as a .json file; on this page it overrides the browser's save dialog, as the help overlay warns.
- ⌘/Ctrl + C
- With nothing selected, copies the active row's value exactly as written in the source — the whole document when no row is active. A real selection copies itself, untouched.
- ⌘/Ctrl + Shift + C
- Copy the entire document, whatever is selected or active.
- t
- Cycle the color theme (single keys stay dead while typing in any field, during IME composition, and when disabled in settings).
- ? or ⌘/Ctrl + /
- Open keyboard help — including the tree's own keys (arrows, Home/End, Enter, type-ahead), the search field's Enter/Shift + Enter stepping, and the ⌘S conflict note.
- Esc
- Close the topmost dialog; it never touches the input or the tree.