JSON HTML Viewer · Typed values and paths
HTML for this example
Select the code and use your copy shortcut. Changes in this preview are temporary.
<!doctype html><html lang="en-US"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>JSON HTML Viewer · Typed values and paths</title><style>*{box-sizing:border-box}html{color-scheme:light}body{margin:0;background:#edf1f4;color:#253846;font:15px/1.5 system-ui,-apple-system,Segoe UI,sans-serif}main{max-width:1220px;margin:auto;padding:26px 28px 42px}header{margin-bottom:22px}h1{font-size:clamp(27px,4vw,36px);line-height:1.15;letter-spacing:-.04em;margin:6px 0 9px}h2{font-size:16px;line-height:1.3;margin:0}p{margin:8px 0 15px}.eyebrow{font-size:11px;font-weight:750;letter-spacing:.14em;text-transform:uppercase;color:#4f6d7f}.subtitle{margin:0;color:#536575}.workspace{display:grid;grid-template-columns:minmax(0,1.25fr) minmax(0,.85fr);grid-template-areas:'overview overview' 'tree selection' 'source source';border:1px solid #bac9d4;border-radius:10px;overflow:hidden;box-shadow:0 7px 25px #2b465509;background:white}.overview{grid-area:overview;display:flex;justify-content:space-between;gap:15px;padding:14px 20px;background:#243d4d;color:white;font-size:13px}.overview strong{font-size:14px}.tree-pane{grid-area:tree;border-right:1px solid #d4dfe6;min-width:0}.pane-head{padding:16px 20px;border-bottom:1px solid #dbe3e9;display:flex;gap:10px;align-items:center;justify-content:space-between}.pane-head small{display:block;font-size:12px;color:#5a6e7b;margin-top:3px}.tools{display:flex;gap:7px;flex-wrap:wrap}.tools button{font-size:12px;padding:7px 10px;min-height:36px;background:#f7f9fa;color:#375a6e;border-color:#c1d0db}button{font:inherit;font-weight:650;background:#315f76;color:white;border:1px solid #315f76;min-height:43px;padding:10px 15px;border-radius:5px;cursor:pointer}button:hover:enabled{filter:brightness(.94)}button:disabled{opacity:.45;cursor:default}.secondary{background:white;color:#315f76;border-color:#b5c7d3}:focus-visible{outline:3px solid #db9b2b;outline-offset:3px}button.node-select:focus-visible{outline-offset:-3px}.tree{padding:12px 16px 20px;max-height:500px;overflow:auto;min-height:310px}.branch{position:relative;min-width:0}.branch>details{margin:0;padding:0;border:0}.branch summary{cursor:pointer;padding:9px 63px 9px 5px;font:13px/1.6 ui-monospace,Consolas,monospace;overflow-wrap:anywhere;list-style-position:inside;border-radius:4px}.branch summary:hover{background:#f0f5f8}.children{padding-left:14px;border-left:1px solid #dbe4eb;margin-left:8px}.branch .branch .branch .branch .children{padding-left:0;margin-left:0;border:0}.select-container{position:absolute;right:0;top:5px;min-height:30px;padding:4px 7px;font-size:11px;background:white;color:#37657b;border-color:#c8d7e1}.node-select{display:block;text-align:left;width:100%;background:transparent;color:#2c4657;border:1px solid transparent;border-radius:4px;font:13px/1.6 ui-monospace,Consolas,monospace;padding:8px 6px;min-height:37px;overflow-wrap:anywhere;white-space:pre-wrap}.node-select[aria-pressed=true],.select-container[aria-pressed=true]{background:#e4eff5;border-color:#85abc0;color:#1f4f68}.tree-empty{padding:16px 6px;color:#657987;font-size:13px}.type-word{font:11px/1.4 system-ui,sans-serif;color:#647988;margin-left:7px}.selection{grid-area:selection;min-width:0;padding:19px 22px 24px;background:#f7fafc}.selection h2{font-size:12px;text-transform:uppercase;letter-spacing:.07em;color:#526d7f}.type{display:inline-block;font-size:12px;border:1px solid #a6c0cf;border-radius:20px;padding:4px 10px;margin:18px 0 9px;background:#edf4f7}.selected-value{font:clamp(25px,3vw,39px)/1.25 ui-monospace,Consolas,monospace;font-weight:650;margin:8px 0 22px;white-space:pre-wrap;overflow-wrap:anywhere;max-height:220px;overflow:auto}.selection h3{font-size:13px;font-weight:700;margin:20px 0 8px}.path{font:13px/1.7 ui-monospace,Consolas,monospace;padding:12px;background:white;border:1px solid #cfdee7;border-radius:5px;margin:0;white-space:pre-wrap;overflow-wrap:anywhere;max-height:190px;overflow:auto}.selection .hint{font-size:12px;color:#587180;margin:9px 0}.source{grid-area:source;border-top:1px solid #c9d6df;padding:20px}.source-top{display:flex;align-items:baseline;justify-content:space-between;gap:15px;margin-bottom:10px}.source-top label{font-weight:750}.source-top span{font-size:12px;color:#647785}textarea{width:100%;display:block;min-height:225px;resize:vertical;border:1px solid #adbfcb;border-radius:6px;background:#fcfdfd;color:#203747;padding:14px;font:14px/1.7 ui-monospace,Consolas,monospace;tab-size:2;white-space:pre-wrap;overflow-wrap:anywhere}textarea:focus-visible{outline-offset:2px}.projection-warning{font-size:13px;color:#536774;margin:12px 0}.projection-warning strong{color:#304e61}.actions{display:flex;gap:10px;flex-wrap:wrap}.error{color:#a2342c;font-weight:650;font-size:14px}.error:not(:empty){padding:12px 14px;background:#fff0eb;border-left:4px solid #af493d;margin:12px 0 0}.notes{margin:24px 0 0;padding:20px 0;border-top:1px solid #c8d6df}.notes summary{font-size:14px;font-weight:750;cursor:pointer}.notes p{font-size:14px;color:#526875;max-width:900px}.notes a{color:#2c627e}footer{font-size:12px;color:#627786;margin-top:22px}[hidden]{display:none!important}@media(min-width:1100px){.workspace{grid-template-columns:minmax(0,1fr) minmax(0,1.2fr) minmax(0,.9fr);grid-template-areas:'overview overview overview' 'source tree selection'}.source{border:0;border-right:1px solid #d4dfe6;padding:20px 16px}.tree-pane{border-right:1px solid #d4dfe6}.source-top{display:block}.source-top span{display:block;margin-top:5px}.source textarea{min-height:320px}.source .actions{gap:7px}.source button{font-size:13px;padding:9px 12px}.tree{max-height:590px;min-height:460px}.overview{padding:15px 20px}.pane-head{display:block}.tools{margin-top:12px}}@media(max-width:720px){main{padding:20px}.selection{padding:18px}.pane-head{padding:15px;display:block}.tools{margin-top:10px}.tree{padding:10px;max-height:430px}.overview{padding:13px 16px;flex-wrap:wrap;gap:4px}.selected-value{font-size:29px}.path{font-size:12px}}@media(max-width:540px){main{padding:16px}.workspace{grid-template-columns:minmax(0,1fr);grid-template-areas:'overview' 'selection' 'tree' 'source'}.selection{border-bottom:1px solid #cfdee6;padding:16px}.selection .type{margin:12px 0 4px}.selected-value{font-size:34px;margin:7px 0 14px;max-height:140px}.selection h3{margin:12px 0 7px}.path{max-height:130px;padding:10px}.selection .hint{margin-bottom:0}.tree-pane{border:0}.pane-head{display:flex;flex-wrap:wrap}.tools{margin:0}.tree{min-height:0;max-height:390px}.source{padding:16px}.source-top{display:block}.source-top span{display:block;margin-top:5px}.children{padding-left:10px;margin-left:5px}textarea{font-size:13px;min-height:230px}.overview{font-size:12px}.notes{margin-top:20px}}</style></head><body><main><header><div class="eyebrow">Paste-only developer inspector</div><h1>JSON, with its location intact.</h1><p class="subtitle">Explore nested values without replacing the source you pasted.</p></header><section class="workspace" aria-label="JSON inspection workspace"><div class="overview"><strong id="rootOverview"></strong><span id="stats" role="status" aria-live="polite"></span></div><section class="tree-pane" aria-labelledby="treeTitle"><div class="pane-head"><div><h2 id="treeTitle">Parsed structure</h2><small>Open a branch; inspect any value.</small></div><div class="tools"><button type="button" id="expand">Expand all</button><button type="button" id="collapse">Collapse all</button></div></div><div id="tree" class="tree"></div></section><section class="selection" aria-labelledby="selectedTitle"><h2 id="selectedTitle">Selected value</h2><div id="selectedType" class="type" hidden></div><pre id="selectedValue" class="selected-value" aria-live="polite"></pre><div id="pathGroup" hidden><h3>Custom inspection path · key/index tokens</h3><pre id="selectedPath" class="path" tabindex="0" aria-label="Selected custom typed token path"></pre><p class="hint">Not JSON Pointer or JSONPath. An empty token list selects the root.</p></div><p id="selectionHint" class="hint"></p></section><section class="source"><div class="source-top"><label for="source">Raw JSON source</label><span id="sourceCount"></span></div><textarea id="source" spellcheck="false" autocomplete="off" aria-describedby="projectionWarning">{
"items": [
{
"id": "007",
"active": false,
"count": 0,
"note": null
}
],
"a/b": {
"~key": "<b>literal</b>"
},
"empty": []
}</textarea><p id="projectionWarning" class="projection-warning"><strong>Parsed projection, not a lossless copy.</strong> Duplicate-key detail, source order, number spelling, and numeric precision may change. The pasted source stays authoritative; this field does not preserve original file bytes.</p><div class="actions"><button type="button" id="parse">Parse JSON</button><button type="button" id="clear" class="secondary">Clear</button><button type="button" id="fixture" class="secondary">Load fixture</button></div><p id="error" class="error" role="alert"></p></section></section><details class="notes"><summary>Bounds, numeric limits, and source notes</summary><p>Before parsing: at most 20,000 UTF-16 code units. Before displaying: at most 500 value nodes and depth 20, counting the root as one node at depth zero. Keys do not add value nodes. Invalid input clears the previous tree and selection; nothing is truncated into a partial document.</p><p>Parsed nonfinite numbers and integers outside JavaScript’s safe-integer range are rejected. If a large identifier is textual, quote it at its source; this viewer never changes its type for you. Ordinary decimals can still approximate, and duplicate keys are not detected. Object ordering and numeric spelling reflect a JavaScript projection, not original source bytes.</p><p>All keys, values, and paths are displayed as literal text. Only own object properties and actual array elements appear. There is no file upload, remote fetch, export, automatic redaction, storage, or lossless round-trip guarantee. These are bounded display rules, not a streaming parser or a complete resource/security guarantee.</p><p>Reference: <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse">MDN JSON.parse</a> and <a href="https://www.rfc-editor.org/rfc/rfc8259">RFC 8259</a>. The initial fictional fixture has ten nodes, maximum depth three, and an identifier string with leading zeros.</p></details><footer>Local session only · No information is sent or saved. Actual browser and host review remain separate.</footer></main><script>const $=id=>document.getElementById(id);const fixtureText="{\n \"items\": [\n {\n \"id\": \"007\",\n \"active\": false,\n \"count\": 0,\n \"note\": null\n }\n ],\n \"a/b\": {\n \"~key\": \"<b>literal</b>\"\n },\n \"empty\": []\n}";let projection=null,selected=null;const buttons=[];const branches=[];
function kind(value){return value===null?'Null':Array.isArray(value)?'Array':typeof value==='object'?'Object':typeof value==='string'?'String':typeof value==='boolean'?'Boolean':'Number'}
function inspectSource(text){if(text.length>20000)throw Error('Input exceeds 20,000 UTF-16 code units. Keep the full source; reduce the document before parsing.');if(!text.trim())throw Error('Paste a JSON value. A blank source is not null or zero.');let value;try{value=JSON.parse(text)}catch(e){throw Error('Invalid JSON: '+e.message.slice(0,240))}const nodes=[],stack=[{value,path:[],depth:0,parent:null,label:'Root'}];let maxDepth=0;while(stack.length){const node=stack.pop();if(node.depth>20)throw Error('Depth exceeds 20 (root is depth 0). No partial tree is displayed.');if(nodes.length>=500)throw Error('Node count exceeds 500, including the root. No partial tree is displayed.');if(typeof node.value==='number'){if(!Number.isFinite(node.value))throw Error('Parsed number is nonfinite. Supply a supported finite number; retain the raw source.');if(Number.isInteger(node.value)&&!Number.isSafeInteger(node.value))throw Error('Parsed integer is outside JavaScript’s safe-integer range. If it is a textual identifier, quote it at its source; no type was changed.')}node.id=nodes.length;node.type=kind(node.value);node.count=node.type==='Array'?node.value.length:node.type==='Object'?Object.keys(node.value).length:null;nodes.push(node);maxDepth=Math.max(maxDepth,node.depth);if(node.type==='Array'){for(let i=node.value.length-1;i>=0;i--)stack.push({value:node.value[i],path:[...node.path,{index:i}],depth:node.depth+1,parent:node.id,label:'['+i+']'})}else if(node.type==='Object'){const keys=Object.keys(node.value);for(let i=keys.length-1;i>=0;i--){const key=keys[i];stack.push({value:node.value[key],path:[...node.path,{key}],depth:node.depth+1,parent:node.id,label:JSON.stringify(key)})}}}return {value,nodes,maxDepth,raw:text}}
function valueText(node){if(node.type==='Array')return '[] · '+node.count+' '+(node.count===1?'element':'elements');if(node.type==='Object')return '{} · '+node.count+' '+(node.count===1?'property':'properties');if(node.type==='Number'&&Object.is(node.value,-0))return '-0';return JSON.stringify(node.value)}
function make(tag,text,className){const e=document.createElement(tag);if(text!==undefined)e.textContent=text;if(className)e.className=className;return e}
function selectNode(id){if(!projection||!Number.isInteger(id)||id<0||id>=projection.nodes.length)return false;selected=id;const node=projection.nodes[id];$('selectedType').textContent=node.type;$('selectedType').hidden=false;$('selectedValue').textContent=valueText(node);$('selectedPath').textContent=JSON.stringify(node.path);$('pathGroup').hidden=false;$('selectionHint').textContent='Depth '+node.depth+' · '+(node.path.length?'Selected parsed value; raw source is unchanged.':'Root value; no wrapper was added.');buttons.forEach((b,i)=>b.setAttribute('aria-pressed',String(i===id)));return true}
function clearProjection(message){projection=null;selected=null;buttons.length=0;branches.length=0;$('tree').replaceChildren();$('selectedType').textContent='';$('selectedType').hidden=true;$('selectedValue').textContent='No selected value';$('selectedPath').textContent='';$('pathGroup').hidden=true;$('selectionHint').textContent='Parse the current source to inspect a value.';$('rootOverview').textContent='No current projection';$('stats').textContent=message;$('expand').disabled=true;$('collapse').disabled=true}
function sourceCount(){$('sourceCount').textContent=$('source').value.length.toLocaleString('en-US')+' / 20,000 UTF-16 units'}
function renderTree(){const hosts=[];$('tree').replaceChildren();buttons.length=0;branches.length=0;for(const node of projection.nodes){const host=node.parent===null?$('tree'):hosts[node.parent],container=node.type==='Array'||node.type==='Object';if(container){const wrapper=make('div',undefined,'branch'),details=make('details'),summary=make('summary',node.label+' · '+node.type+' · '+node.count+' '+(node.type==='Array'?'elements':'properties')),children=make('div',undefined,'children'),button=make('button','Inspect','select-container');details.open=node.depth<4;button.type='button';button.id='node'+node.id;button.setAttribute('aria-label','Inspect '+node.label+', '+node.type+', depth '+node.depth);button.setAttribute('aria-pressed','false');button.addEventListener('click',()=>selectNode(node.id));details.appendChild(summary);details.appendChild(children);wrapper.appendChild(details);wrapper.appendChild(button);host.appendChild(wrapper);hosts[node.id]=children;buttons[node.id]=button;branches.push(details);if(node.count===0)children.appendChild(make('p',node.type==='Array'?'[] · No elements':'{} · No properties','tree-empty'))}else{const button=make('button',node.label+': '+valueText(node)+' · '+node.type,'node-select');button.type='button';button.id='node'+node.id;button.setAttribute('aria-label','Inspect '+node.label+', '+node.type+', depth '+node.depth);button.setAttribute('aria-pressed','false');button.addEventListener('click',()=>selectNode(node.id));host.appendChild(button);buttons[node.id]=button}}}
function parseSource(preferFixture=false){clearProjection('Parsing current source…');$('error').textContent='';sourceCount();try{const checked=inspectSource($('source').value);projection=checked;renderTree();const root=projection.nodes[0];$('rootOverview').textContent='Root: '+root.type+(root.count===null?'':' · '+root.count+' '+(root.type==='Array'?'elements':'properties'));$('stats').textContent=projection.nodes.length+' value '+(projection.nodes.length===1?'node':'nodes')+' · maximum depth '+projection.maxDepth;$('expand').disabled=!branches.length;$('collapse').disabled=!branches.length;const path=[{key:'items'},{index:0},{key:'id'}],initial=preferFixture?projection.nodes.find(n=>JSON.stringify(n.path)===JSON.stringify(path)):null;selectNode(initial?initial.id:0)}catch(e){clearProjection('No projection · source needs review');$('error').textContent=e.message}}
function invalidate(){$('error').textContent='';sourceCount();clearProjection('Source changed · Parse JSON to rebuild')}
$('source').addEventListener('input',invalidate);$('parse').onclick=()=>parseSource();$('clear').onclick=()=>{$('source').value='';$('error').textContent='';sourceCount();clearProjection('Source cleared')};$('fixture').onclick=()=>{$('source').value=fixtureText;parseSource(true)};$('expand').onclick=()=>{if(!projection)return;branches.forEach(d=>d.open=true)};$('collapse').onclick=()=>{if(!projection)return;branches.forEach(d=>d.open=false)};$('source').value=fixtureText;parseSource(true);</script></body></html>