JSON Viewer (Tree View)
100% in your browser
Your data is never uploaded. All processing happens locally on your device.
Read privacy policy →Keyboard shortcuts
Last updated: 2026-09-17
Paste JSON to explore it in an interactive, collapsible tree view. Search keys and values, click any node to see its JSONPath, and navigate massive files with virtualized scrolling. All processing is local.
What this tool does
Parses JSON and renders it as an interactive, collapsible tree. Each object and array is a node that can be expanded or collapsed. Clicking any leaf node shows its JSONPath expression. The tree is virtualized, so only visible rows are rendered — files with thousands of nodes scroll smoothly.
Example
Input:
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"geometry": { "type": "Point", "coordinates": [102, 0.5] },
"properties": { "name": "Location A" }
}
]
}The tree renders:
root (object)
├── type: "FeatureCollection"
├── features (array, 1 item)
│ └── [0] (object)
│ ├── type: "Feature"
│ ├── geometry (object)
│ │ ├── type: "Point"
│ │ └── coordinates (array, 2 items)
│ │ ├── [0]: 102
│ │ └── [1]: 0.5
│ └── properties (object)
│ └── name: "Location A"Clicking the name node shows its JSONPath: $.features[0].properties.name
How the tree is built
When you paste JSON, the tool parses the text into an in-memory object graph and walks that graph to build a tree of nodes. Every object becomes a container node whose children are its keys; every array becomes a container node whose children are indexed from zero. Leaf values — strings, numbers, booleans, and null — become terminal nodes. The parser preserves insertion order for object keys, so the tree matches the order in your source text rather than sorting alphabetically. After the graph is built, the renderer assigns each node a stable path string, which is what gets shown as JSONPath when you click a node. For files above roughly two hundred kilobytes, parsing moves to a Web Worker so the main thread keeps painting while the worker deserializes the payload. Because the graph is held in memory after parsing, subsequent operations like search and expansion read from that cached model instead of re-parsing the original text each time.
Tree node types
| Node type | Display | Icon |
|---|---|---|
| Object | key (object) | {} |
| Array | key (array, N items) | [] |
| String | key: “value” | " |
| Number | key: 42 | # |
| Boolean | key: true | T |
| Null | key: null | ∅ |
Exploring API responses
A typical REST endpoint returns paginated data wrapped in metadata. Consider a response from a list-users endpoint:
{
"page": 2,
"per_page": 20,
"total": 347,
"total_pages": 18,
"data": [
{ "id": 21, "email": "ada@example.com", "role": "admin" },
{ "id": 22, "email": "grace@example.com", "role": "member" }
],
"links": {
"self": "https://api.example.com/users?page=2",
"next": "https://api.example.com/users?page=3",
"prev": "https://api.example.com/users?page=1"
}
}In the tree, expand data to see the array of user objects. Each user is an object node with its own fields. To check whether pagination metadata is correct, expand links and compare the next URL against total_pages. If you need the email of the first user on this page, click that node and copy $.data[0].email. To confirm you are not on the last page, click page and compare its numeric value against total_pages — both are numeric leaf nodes rendered near the top of the tree, so the comparison is immediate. The array length next to data tells you how many users came back in this batch without expanding it, which is useful when a page is short and you suspect a filter removed most results.
When an endpoint wraps results inconsistently — sometimes under data, sometimes under results, sometimes under items — search for a key you know belongs to a record, such as email, and the tree reveals which wrapper the server chose for this response. If the server nests errors alongside data, search for the key message or error to locate the diagnostic block without scanning the whole payload. The match counter tells you immediately whether an error surfaced at all.
Exploring GeoJSON
GeoJSON nests geometry and properties inside a features array. A real file often contains hundreds of features, each with its own coordinate array. The tree shows the structure without forcing you to scan a wall of brackets. Example:
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"id": "stop_44",
"geometry": { "type": "Point", "coordinates": [-122.4194, 37.7749] },
"properties": { "name": "Market St", "routes": ["14", "14R", "49"] }
}
]
}Expand features, then the first feature, then geometry to inspect its coordinates. The array length next to coordinates confirms whether the point has two numbers (a Point) or more (a LineString or Polygon). Expand properties and then routes to see the transit lines serving that stop. If you want to extract every coordinate in code, click a coordinates node to get $.features[0].geometry.coordinates, then swap the fixed index for a wildcard in your script. When a file mixes geometry types, search for the key coordinates and step through the matches — the array length on each hit tells you the geometry shape before you expand anything.
Properties differ per feature in real datasets: one stop may carry a routes array while another carries wheelchair_boarding or stop_code. Rather than expanding every feature to learn which fields exist, search for the field name directly. Each hit jumps to the feature that contains it, and the surrounding nodes show the sibling properties available on that particular record. This is faster than opening hundreds of feature objects one by one, especially in files where most properties are sparse.
Using JSONPath in your code
The tree gives you exact paths. Once you have a path, you can extract values programmatically without hand-writing index numbers.
Extract every email from a users array:
const { JSONPath } = require('jsonpath-plus');
const emails = JSONPath.JSONPath({ path: '$.data[*].email', json: response });
// ["ada@example.com", "grace@example.com"]Extract all coordinate pairs from a GeoJSON file:
const coords = JSONPath.JSONPath({ path: '$.features[*].geometry.coordinates', json: geo });Find objects by a property value (filter expression):
const admins = JSONPath.JSONPath({ path: "$.data[?(@.role=='admin')]", json: response });Python equivalent with jsonpath-ng:
from jsonpath_ng.ext import parse
expr = parse("$..coordinates")
coords = [m.value for m in expr.find(geo)]The wildcard [*] iterates array elements, while .. is a recursive descent that matches the key at any depth. Use .. when you do not know the exact nesting level — for example, when a field appears at different paths in different records. The filter syntax [?(@.field=='value')] selects array elements where a condition holds, which is faster than pulling the whole array and filtering in a loop when the array is large. You can combine these: $..features[?(@.properties.name=='Market St')] descends to every features array anywhere in the document and returns only the feature whose name matches. Bracket index slices like [1:3] are also supported by both libraries, letting you grab a contiguous range of elements without looping in your own code.
Common mistakes when navigating JSON
- Treating an object key as an array index.
$.users.emailfails whenusersis an array; you need$.users[0].emailor the wildcard$.users[*].email. - Forgetting that arrays are zero-indexed. The fourth user is
$.users[3], not$.users[4]. - Assuming a field always exists. A path like
$.user.profile.avatarthrows or returns nothing whenprofileis null. Check whether the parent node is null before descending. - Confusing JSONPath with JavaScript property access. JSONPath uses brackets and dollar syntax, not dot-access on an object reference.
- Copying a path from a minified document and missing a level of nesting. Expand the parent nodes in the tree first so the displayed path reflects the true depth.
- Expecting search to only scan what is visible. Search matches every node regardless of collapse state, and the tree expands the parent chain to reveal each hit as you step through results.
- Searching for a value that lives inside a long string. The search matches leaf values, so a URL embedded in a larger block still surfaces, but you may need a smaller unique fragment to avoid hundreds of partial hits.
- Misreading the array count. The number next to an array node is the element count, not a byte size or an index. A label of
array, 3 itemsmeans indices 0 through 2. - Overlooking string versus number ambiguity. A value quoted as
"42"is a string, shown with the string icon, while42is a number. Sorting or arithmetic behave differently, so check the icon before assuming a numeric field. - Ignoring the null icon. A field rendered with the null indicator is not an empty string and not missing; it is an explicit null, which often signals a deliberately unset value rather than a transmission error.
Search tips
- Start with a distinctive fragment. Searching
errorin a large log-shaped document returns many hits; searchingECONNREFUSEDnarrows results to the entries you want. - Search by key when you want every occurrence of a field. Typing
coordinateslists every coordinate array in a GeoJSON file, and next/prev steps through them. - Use value search to verify data. If you expect a specific ID, search for it directly instead of expanding nodes one by one.
- Remember that the search field matches both keys and values. A query like
adminfinds both a key named admin and a value set to admin, so check the hit type when results look mixed. - Watch the match counter. It tells you how many hits exist before you start stepping, so you know whether a missing value means zero results or a typo in your query.
- After landing on a match, copy its JSONPath before moving to the next result. The path updates per clicked node, and stepping to the next match overwrites the current selection.
- For deeply nested documents, search is faster than expanding. A targeted query skips the manual work of opening each ancestor, and the tree auto-expands only the chain leading to the hit.
- Clear the search box to dismiss highlights and the match counter. The tree returns to whatever expand state you left it in; collapsing a branch during search does not re-collapse after clearing.
- When a document contains localized text, search the language-neutral identifier instead of the translated label. A key like
label_enis more stable across records than the prose it points to.
Performance characteristics
Because rendering is virtualized, the cost scales with the number of visible rows, not the total node count. A document with one hundred thousand nodes still only paints the thirty or so rows that fit on screen. Expanding a node only renders its immediate children; collapsing discards their rows from the DOM. Search is the one operation that touches every node, since it must match keys and values across the whole graph, but matching runs against the in-memory model rather than the DOM, so it stays fast even when little is visible. The Web Worker boundary means a multi-megabyte paste deserializes off the main thread; the page reports a loading state until the worker posts the built graph back. Memory use is proportional to the node count, not the file size, because the parsed graph replaces the raw string once parsing finishes.
Comparing the tree view with raw JSON
Use the tree view when the document is large, deeply nested, or unfamiliar. Virtualized rendering keeps scrolling smooth, and collapsible nodes let you hide irrelevant branches. The JSONPath display means you do not have to count brackets to find where a value lives, and the type icons make it obvious whether a value is a string, number, boolean, or null at a glance. Search adds a second entry point: instead of visually scanning for a field, type its name and jump straight to every occurrence, which beats manual expansion when a document spans thousands of rows.
Use raw JSON when you need the exact text — for example, when debugging a serialization bug where whitespace, key order, or duplicate keys matter. The tree normalizes formatting and may not reveal a duplicate object key, because a parser keeps only the last value. Raw JSON is also faster for small documents where the structure is already obvious and you just need to copy a fragment verbatim into a test fixture. It is also the only view that shows number formatting precisely — whether a value was serialized as an integer or a float, or with a specific exponent — since the tree re-renders parsed values.
A practical workflow: paste the document, search for the value you are investigating, click the matching node to get its JSONPath, then switch to raw JSON only if you need to inspect surrounding syntax the tree abstracted away.
Do it in code
JavaScript (jsonpath-plus):
const { JSONPath } = require('jsonpath-plus');
const data = JSON.parse(jsonString);
// Extract a value
const email = JSONPath.JSONPath({ path: '$.users[3].email', json: data });Python (jsonpath-ng):
from jsonpath_ng import parse
import json
data = json.loads(json_string)
expr = parse('$.users[3].email')
matches = [m.value for m in expr.find(data)]CLI (jq):
# Extract a specific path
echo "$json" | jq '.users[3].email'
# jq uses dot notation, which is equivalent to JSONPath without the leading $Edge cases handled
- Deeply nested JSON (10+ levels) renders correctly with indentation.
- Large arrays (thousands of elements) scroll smoothly due to virtualization.
- Unicode values (emoji, CJK, accented characters) display correctly in the tree.
- Null, boolean, and numeric leaf values are displayed with type indicators.
- Array length is shown next to each array node so you know the size without expanding.
- Duplicate object keys are collapsed to the last value during parsing, mirroring standard JSON parser behavior. Inspect the raw text if you suspect duplicates.
- Mixed-type arrays — for example a list containing both objects and strings — render each element with its own type icon, so the heterogeneity is visible without expanding every item.
- Very long string values are truncated in the row preview; expand the node to read the full contents without disrupting the row layout.