NNicheStackTutorials
← All tutorials
Frontend Tools

jsonfmt: A Zero-Dependency JSON Formatter That Survived 40MB Paste Tests

Built a browser JSON formatter after every online tool choked on a minified API dump. Full vanilla JS, syntax validation, and the parse errors that actually matter.

·11 min read·Frontend Tools
jsonfmt: A Zero-Dependency JSON Formatter That Survived 40MB Paste Tests cover

Tested on: Chrome 136, Firefox 139, Safari 18 Problem: Pasted a 38MB minified package-lock.json into three online formatters. Two tabs froze. One showed Unexpected token with no line number.

Needed something I could host on a static page with zero npm install for visitors.

What it does

Paste ugly JSON → click Format → pretty-print with 2-space indent
Invalid JSON → red error box with line/column from native SyntaxError

Full single-file tool (copy-paste)

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>JSON Formatter</title>
  <style>
    * { box-sizing: border-box; }
    body { font-family: system-ui, sans-serif; max-width: 900px; margin: 2rem auto; padding: 0 1rem; }
    textarea { width: 100%; min-height: 220px; font-family: ui-monospace, monospace; font-size: 13px; }
    .row { display: flex; gap: .5rem; margin: .75rem 0; flex-wrap: wrap; }
    button { padding: .5rem 1rem; cursor: pointer; }
    #error { color: #b91c1c; background: #fef2f2; padding: .75rem; border-radius: 6px; display: none; }
    #error.show { display: block; }
    label { font-size: .875rem; }
  </style>
</head>
<body>
  <h1>JSON Formatter</h1>
  <textarea id="input" placeholder='{"key":"value"}'></textarea>
  <div class="row">
    <button type="button" id="format">Format</button>
    <button type="button" id="minify">Minify</button>
    <button type="button" id="clear">Clear</button>
    <label><input type="number" id="indent" value="2" min="0" max="8" style="width:3rem"> spaces</label>
  </div>
  <div id="error" role="alert"></div>
  <textarea id="output" readonly placeholder="Output"></textarea>
  <script>
    const input = document.getElementById('input');
    const output = document.getElementById('output');
    const errorEl = document.getElementById('error');
    const indentEl = document.getElementById('indent');

    function showError(msg) {
      errorEl.textContent = msg;
      errorEl.classList.add('show');
      output.value = '';
    }

    function clearError() {
      errorEl.textContent = '';
      errorEl.classList.remove('show');
    }

    function parseJson(text) {
      try {
        return JSON.parse(text);
      } catch (e) {
        const match = e.message.match(/position (\d+)/i);
        if (match) {
          const pos = Number(match[1]);
          const before = text.slice(0, pos);
          const line = before.split('\n').length;
          const col = before.length - before.lastIndexOf('\n');
          throw new Error(e.message + ' (line ' + line + ', col ' + col + ')');
        }
        throw e;
      }
    }

    document.getElementById('format').addEventListener('click', () => {
      clearError();
      const raw = input.value.trim();
      if (!raw) return;
      try {
        const obj = parseJson(raw);
        const spaces = Number(indentEl.value) || 0;
        output.value = JSON.stringify(obj, null, spaces);
      } catch (e) {
        showError(e.message);
      }
    });

    document.getElementById('minify').addEventListener('click', () => {
      clearError();
      const raw = input.value.trim();
      if (!raw) return;
      try {
        output.value = JSON.stringify(parseJson(raw));
      } catch (e) {
        showError(e.message);
      }
    });

    document.getElementById('clear').addEventListener('click', () => {
      input.value = '';
      output.value = '';
      clearError();
    });
  </script>
</body>
</html>

Error I kept hitting

JSON.parse: Unexpected token u in JSON at position 0

Cause: pasted undefined from a console.log. JSON has no undefined — only null.

Trailing comma trap

{"a": 1,}

Strict JSON.parse rejects trailing commas. My error handler now surfaces line/col so you find the comma in seconds.

Performance note

For files over ~50MB, add a warning:

if (raw.length > 50_000_000) {
  if (!confirm('Large input may freeze the tab. Continue?')) return;
}

38MB formatted in ~800ms on M2 MacBook. Acceptable for a static tool page.

Verify

  1. Save as jsonfmt.html, open via npx serve .
  2. Paste invalid {"a":} — error shows line 1
  3. Paste valid minified JSON — output is indented
  4. Lighthouse: 100 Performance (no external scripts)

Deploy

Drop the file in static/tools/jsonfmt/index.html on Hugo/VitePress/Astro. One HTML file, no build step.

Tags: JSONVanilla JSBrowser ToolsStatic Site