Embed a JSON Snippet in Your Page with an iframe

You have a JSON snippet on your page — an API response in your docs, a config example in a blog post, a webhook payload in a tutorial — and you want readers to explore it as a collapsible tree instead of staring at a wall of text. Big JSON Viewer has an /embed route built for exactly this. Point an <iframe> at it, put the JSON in the URL, and you get a read-only tree view that readers can click through to the full app.

This post is the integration spec: the URL format, how to build the payload, and the constraints to know about.

The shape of an embed URL

An embed URL has exactly one moving part — the data field in the URL hash:

https://www.bigjsonviewer.com/embed/#data=<payload>

The payload is your JSON, compressed and encoded:

payload = "v1." + base64url( gzip( utf8( jsonText ) ) )

Three things stacked: gzip the UTF-8 JSON bytes, base64url-encode the result, then prefix the literal string v1. as a format marker. That’s the whole format.

Why the hash, and why gzip

The payload lives after the # on purpose. A URL hash is never sent to the server — not to ours, not to any proxy or CDN in between. Your JSON never lands in an access log, a Referer header, or a server-side cache. It is decoded entirely in the reader’s browser. That also sidesteps the ~2–8 KB length ceiling that servers and gateways enforce on the path and query string; only the browser’s much higher hash limit applies.

Gzip is what keeps the URL short enough to be practical. JSON compresses extremely well — a 5 KB snippet typically lands under 200 characters of payload. base64url (the URL-safe base64 variant: - and _ instead of + and /, no = padding) means the encoded blob drops straight into the hash with no further escaping.

Building the payload in the browser

Browsers ship native streaming gzip via CompressionStream, so there is no dependency to pull in:

async function encodeJsonForHash(jsonText) {
    // gzip the UTF-8 bytes
    const stream = new Blob([jsonText]).stream()
        .pipeThrough(new CompressionStream('gzip'));
    const bytes = new Uint8Array(await new Response(stream).arrayBuffer());

    // base64url-encode (chunked to avoid call-stack overflow on large input)
    let bin = '';
    const CHUNK = 0x8000;
    for (let i = 0; i < bytes.length; i += CHUNK) {
        bin += String.fromCharCode(...bytes.subarray(i, i + CHUNK));
    }
    const b64url = btoa(bin)
        .replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');

    return 'v1.' + b64url;
}

const url = 'https://www.bigjsonviewer.com/embed/#data=' +
    await encodeJsonForHash(JSON.stringify(myObject));

Building the payload on a server (Node.js)

Most embeds are generated server-side — your docs build step, your CMS, your backend. Node’s built-in zlib produces standard gzip that the browser’s DecompressionStream reads without any special handling, and Buffer has base64url built in:

import { gzipSync } from 'node:zlib';

function encodeJsonForHash(jsonText) {
    const gz = gzipSync(Buffer.from(jsonText, 'utf8'));
    return 'v1.' + gz.toString('base64url');
}

const payload = encodeJsonForHash(JSON.stringify(myObject));
const src = `https://www.bigjsonviewer.com/embed/#data=${payload}`;

Any language works the same way — gzip, base64url, prepend v1.. The decoder only cares that the bytes are valid gzip and the prefix matches.

The iframe

Drop the URL into a standard iframe. The /embed route sends Content-Security-Policy: frame-ancestors *, so it is framable from any origin:

<iframe
    src="https://www.bigjsonviewer.com/embed/#data=v1.H4sIAAAA..."
    width="100%"
    height="420"
    style="border:0;border-radius:8px"
    loading="lazy"
    title="Big JSON Viewer"
></iframe>

The frame renders just the tree — no header, no tabs, no upload chrome — with fold/expand and copy-on-hover. In the corner is an Open in Big JSON Viewer link that forwards the same payload to the full app (search, depth control, split tabs, the lot). Because it reuses the identical hash, the click-through costs no re-encode and no server round-trip.

If you’d rather not hand-build the payload, open any JSON in the app, open the menu in the header, and click Embed — it generates the iframe snippet for you, with an adjustable height and theme.

Matching your page’s theme

The embed resolves its light/dark theme in three layers, so it fits whatever your page does — from zero config to fully driven:

  • System (default). With no extra config the viewer follows prefers-color-scheme and flips live when the OS theme changes. If your page already respects the system theme, the iframe matches it for free.
  • Pinned via URL. Add &theme=light or &theme=dark to the hash to lock the embed to one theme — use this when your page’s theme is fixed: /embed/#data=v1.…&theme=dark.
  • Driven live via postMessage. If your page has its own theme toggle, post a message to the iframe whenever it changes and the embed updates instantly:
const frame = document.querySelector('iframe');
frame.contentWindow.postMessage(
    { type: 'bjv:theme', theme: 'dark' },  // 'light' | 'dark' | 'auto'
    'https://www.bigjsonviewer.com'
);

postMessage wins over the URL param, which wins over the system default. Note the embed deliberately ignores any theme a visitor saved on bigjsonviewer.com itself — that personal preference shouldn’t leak into your page.

Constraints worth knowing

  • Size cap: 256 KB of raw JSON. URL hashes can hold far more, but a giant base64 blob bloats your page’s HTML and gets unwieldy to copy. Past the cap, the in-app generator steers you to the Drive-backed Share flow instead, which handles files up to 500 MB. The embed route is for snippets; Share is for files.
  • The v1. prefix is load-bearing. It is how the decoder knows the format. Omit it and decoding fails. It also future-proofs the format: a later v2. can change the encoding without breaking links already minted as v1..
  • Locale prefix is optional. Prepend a supported locale to open the viewer in that language — /zh-CN/embed/#data=…, /ja/embed/#data=…. English is the bare /embed/.
  • It must be valid JSON. The viewer runs JSON.parse on the decoded text; comments, trailing commas, and JS object literals won’t load.

That’s the whole contract

A URL of the form /embed/#data=v1.<base64url-gzip>, a 256 KB ceiling, and an iframe. No API key, no account, no upload, no server in the loop. The JSON lives in the link and nowhere else — which is exactly the property you want when you’re putting someone else’s payload on your page.