Four numbers you can reproduce
One synthetic dataset, with key counts of 1,000, 10,000, 100,000 and 200,000. Clicking "Format view" measured 12.5 ms, 102.2 ms, 938.6 ms and 1853.9 ms, and the container held 4,007, 40,007, 400,007 and 800,007 elements respectively; in the last case the json-entry rows number 200,000. The four points form a straight line, and no segment bends into a "stop after the first N entries" knee - the template genuinely contains no such branch. Every entry always produces 1 div plus 3 spans, so the element count is roughly four times the key count. That is arithmetic dictated by the implementation, not a matter of optimization.
Fig. 1: render time climbs linearly with the key count; even at 200,000 keys everything still lands in the DOM.
| Key count | Format time | Elements in container |
|---|---|---|
| 1000 | 12.5 ms | 4007 |
| 10000 | 102.2 ms | 40007 |
| 100000 | 938.6 ms | 400007 |
| 200000 | 1853.9 ms | 800007 |
No truncation cap, but the first screen may show nothing
"Rendering everything" is not the same as "showing everything". When the 100,000-key dataset finishes rendering, the default state is fully collapsed: 0 entries are visible on the first screen and the container's scrollHeight is only 298 px. Over a million characters have already become DOM, yet the page occupies a short strip. After clicking "Expand all", scrollHeight becomes 2,409,443 px; at 24.09 px per row that is exactly 4,015.7 times a 600 px viewport. So to judge whether this view will strain the browser, watch the total element count, not the scrollbar; and a short scrollbar does not mean the data never arrived. The question worth testing is at what input size the JSON view starts to slow down, not whether it secretly truncates.
Deep nesting: valid JSON reported as a syntax error
Beyond scale there is a second trap, and it is nastier. Building purely nested objects and re-testing the same depth 10 times in a row gives: 2,000 levels overflow once, 4,000 levels pass all 10 runs, 6,000 levels blow all 10 runs. Take a different call path and three different critical depths appear: 2,886, 2,896 and 2,930. The threshold is not one fixed number; it drifts with how many frames the call stack has already eaten, so the same text may pass this run and fail the next.
When the stack blows, the page prints exactly this line:
❌ JSON语法错误:Maximum call stack size exceeded
The input itself is syntactically valid, yet the message files it under "syntax error", and the render area keeps only the words "JSON format error". The real difference: a syntax error is something you fix by editing one character, while a stack overflow is the recursive implementation hitting the engine's stack limit - at the same depth, a different entry point may sail through. When you see that sentence, suspect the nesting depth first, never the commas.
Fig. 2: overflow counts across 10 repeats at the same depth; the threshold drifts with the call path.
Duplicate keys leave one value in the view
Input like {"a":1,"a":2,"b":3} is syntactically valid, and parsing lets the later a overwrite the earlier one. The view keeps only 2 rows, a shows 2, and the badge reads " 2项": the overwritten 1 leaves no trace anywhere - no warning, no highlight, nothing kept in the collapsed header. A related gap shows up with empty containers: {} and [] get no "0 items" badge, because line 105 of the template outputs only a single space, so the item count simply is absent for an empty object. To check whether a key got swallowed, compare against the source text, never against the view.
Search hits text nodes, and the counter undercounts
Search walks text nodes with TreeWalker and does not separate key names from values: searching for 项 hits the count badge produced at line 112, while searching { hits the brace itself. The counter deserves more caution. In {"s":"aba"}, searching for a paints 2 highlights on the page but the result bar says "1 match": line 251 takes only the first hit per node. The scale numbers confirm this path is not cheap either: searching key9 in a 100,000-key tree yields 11,111 matches in 1071.3 ms, and container elements grow from 400,007 to 422,229 - the extras are exactly those highlight nodes. Read the match count as "occurrences found" only after remembering it may be an undercount.
Copy returns regenerated text
The Copy button on the right runs JSON.stringify(jsonData, null, 2); it does not hand back your original text. The 100,000-key source is 1,677,781 characters; the copy yields 2,077,782 characters, and the extra 400,001 are all 2-space indentation plus line breaks. Input that used 4 spaces or tabs is normalized to the same style. The identically named button on the left copies the raw text - same label, different product. It also unescapes on the way out: the 27 characters of {"s":"\u4e2d\u6587 \u0041"} come back as 17 characters, "中文 A". Key order follows parse order and is not re-sorted. As for the "path copy" promised in the tool's introduction, UU.copy appears exactly once in the template, at line 327; that action does not exist today.
Whether invalid JSON gets a line and column depends on the engine
Of 12 invalid samples, 10 come back with position, line and column and can be pinned directly; but "Unexpected end of JSON input" and the trailing comma in a 5-line input only echo a slice of the source and give no character position at all. Lines 84-88 of the template just concatenate e.message onto the page, with no code of its own to compute row and column. The precision of the location is entirely whatever branch V8 happened to take; the tool neither can nor promises to make it exact.
Numbers in the display layer: String() only
All the view does to numbers is String(): {"n":1e400} shows as Infinity in the view and copies out as null; 12345678901234567890 displays as 12345678901234567000. The value was already changed at the moment of parsing, and the ones that changed it are JSON.parse and JSON.stringify, not the view. Why the precision goes missing has been covered in full elsewhere, so it is not repeated here. This post deals only with the scale and display-layer side; for how collapse, expand, search and copy fit together as a reading workflow, see JSON too long to find a field? Locating entries with the tree view, collapse and search.
You might also want to know
Q: Is there a safe key-count ceiling?
No hard limit. Rendering 200,000 keys in 1853.9 ms is slow, not broken; what actually breaks is nesting depth, and it cannot produce a stable threshold even at one fixed depth.
Q: Does "Maximum call stack size exceeded" mean my data is corrupt?
No. That message covers input that is syntactically valid but too deeply nested; re-testing the same depth through a different call path may pass.
Q: How do I get back text identical to what I pasted?
Use the left-hand button with the same label to copy the raw text, not the right-hand one that runs stringify; when comparing indentation, compare character counts first and content second.