JSON Viewer (Tree View)

Output
Indent:
Examples:

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 typeDisplayIcon
Objectkey (object){}
Arraykey (array, N items)[]
Stringkey: “value”"
Numberkey: 42#
Booleankey: trueT
Nullkey: 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

Search tips

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

Frequently asked questions

How large a file can the tree view handle?
The tree is virtualized — only visible rows are rendered. This means it can handle files with hundreds of thousands of nodes without performance issues. Parsing happens in a Web Worker for files over 200KB so the UI never freezes.
What is JSONPath?
JSONPath is a notation for addressing specific elements in a JSON document, similar to XPath for XML. For example, $.users[3].email points to the email field of the fourth user in the users array. Click any node in the tree to get its JSONPath.
Can I search both keys and values?
Yes. The search box matches against both object keys and leaf values. Match counts and next/prev navigation let you step through all results in the tree.
How do I expand collapsed nodes?
Click the arrow next to any object or array to expand it. You can also use Expand All to open every node, or Expand to Depth N to open nodes up to a specific nesting level without overwhelming the browser.
Does the tree view modify my data?
No. The tree is a read-only view of your JSON. Navigating, expanding, and searching do not change the original input in any way. You can copy JSONPath expressions from the tree to use in your code.
How do I extract a value from JSON using JSONPath in code?
In JavaScript, use jsonpath-plus: const result = JSONPath.JSONPath({path: '$.users[3].email', json: data}). In Python, use the jsonpath-ng package. Both support the same JSONPath syntax that the tree view displays.
What happens with invalid or malformed JSON?
If the input cannot be parsed, the viewer reports a parse error and points to the line and column where parsing stopped. The original text stays in the input area so you can correct the syntax and re-parse. Common causes are trailing commas, single quotes around strings, and unescaped control characters inside string values.
Does the tool work offline?
Yes. Parsing, tree building, search, and JSONPath generation all run locally in the browser. No JSON is sent to a server. For files over 200KB, parsing runs in a Web Worker so the main thread stays responsive, but the work still happens on your machine.

Related tools