How to Build a JSON Formatter

A step-by-step guide to building a fully functional JSON formatter, validator, and minifier — zero dependencies.

~7 min read • October 2026

javascript json tutorial frontend

In This Tutorial

  1. Why Build a JSON Formatter?
  2. Step 1: HTML Layout
  3. Step 2: Parse and Validate
  4. Step 3: Beautify (Pretty-Print)
  5. Step 4: Minify
  6. Step 5: Syntax Highlighting
  7. Step 6: Error Handling
  8. The Complete Solution
  9. Try It Live

Why Build a JSON Formatter?

JSON is the lingua franca of the web. Every API returns it, every config file uses it, and every developer has spent time staring at a wall of unreadable JSON trying to find the missing comma.

Most JSON formatters online are either bloated web apps full of ads, or they send your data to a remote server. Building your own gives you:

This tutorial teaches you about error handling, string manipulation, and the core JSON API that powers every modern web application.

Step 1: The HTML Layout

A JSON formatter needs an input area, an output area, and action buttons. Here's the structure:

HTML"><div class="json-formatter">
  <!-- Action buttons -->
  <div class="toolbar">
    <button id="format-btn">Format</button>
    <button id="minify-btn">Minify</button>
    <button id="copy-btn">Copy</button>
    <button id="clear-btn">Clear</button>
  </div>

  <!-- Input/Output panels -->
  <div class="panels">
    <div class="panel">
      <label>Input JSON</label>
      <textarea id="input" placeholder='{"key": "value"}'></textarea>
    </div>

    <div class="panel">
      <label>Output</label>
      <pre id="output"></pre>
    </div>
  </div>

  <!-- Status bar -->
  <div id="status" class="status">Enter JSON above and click Format</div>
</div>

Key design decisions:

Step 2: Parse and Validate

Every JSON operation starts with parsing. JSON.parse() either returns your data or throws an error:

JavaScript">// Core parsing function — returns { success, data, error }
function parseJSON(input) {
  try {
    const data = JSON.parse(input);
    return { success: true, data: data, error: null };
  } catch (error) {
    return {
      success: false,
      data: null,
      error: error.message
    };
  }
}

// Usage
const result = parseJSON('{"name": "K1R4", "version": 1}');
// result = { success: true, data: { name: "K1R4", version: 1 }, error: null }

const badResult = parseJSON('{name: K1R4}');
// badResult = { success: false, data: null, error: "Unexpected token n..." }

Key insight: JSON.parse() is synchronous and throws on invalid JSON. The try/catch pattern is the only way to handle invalid input gracefully. Always wrap it in try/catch — never assume the input is valid.

Step 3: Beautify (Pretty-Print)

Beautifying JSON is deceptively simple: parse it, then stringify with indentation:

JavaScript">// Beautify: parse then re-stringify with indentation
function beautifyJSON(input, indent = 2) {
  const result = parseJSON(input);
  if (!result.success) {
    throw new Error(result.error);
  }
  return JSON.stringify(result.data, null, indent);
}

// Example:
const raw = '{"users":[{"name":"Alice"},{"name":"Bob"}]}';
beautifyJSON(raw, 2);
// Returns:
// {
//   "users": [
//     {
//       "name": "Alice"
//     },
//     {
//       "name": "Bob"
//     }
//   ]
// }

// With 4-space indentation:
beautifyJSON(raw, 4);
// Returns:
// {
//     "users": [
//         {
//             "name": "Alice"
//         },
//         {
//             "name": "Bob"
//         }
//     ]
// }

The second argument to JSON.stringify() — null — is the replacer parameter. When it's null, all properties are included. You can pass a function or array to filter specific keys.

The third argument to JSON.stringify() controls indentation. Use 2 spaces for web APIs (smaller payload), 4 spaces for readability (code reviews, config files). The default is 2.

Step 4: Minify

Minifying is the reverse: parse then stringify with no whitespace:

JavaScript">// Minify: parse then re-stringify with zero whitespace
function minifyJSON(input) {
  const result = parseJSON(input);
  if (!result.success) {
    throw new Error(result.error);
  }
  return JSON.stringify(result.data);
}

// Example:
const pretty = JSON.stringify({
  users: [
    { name: "Alice" },
    { name: "Bob" }
  ]
}, null, 2);

// pretty = '{\n  "users": [\n    {\n      "name": "Alice"\n    },\n    {\n      "name": "Bob"\n    }\n  ]\n}'

minifyJSON(pretty);
// Returns: '{"users":[{"name":"Alice"},{"name":"Bob"}]}'

Minification removes all whitespace, newlines, and indentation. The resulting string is functionally identical but significantly smaller — important for API payloads and storage.

Step 5: Syntax Highlighting

A formatted JSON output is much easier to read with syntax highlighting. Here's a lightweight approach using regex:

JavaScript">// Lightweight JSON syntax highlighter
function highlightJSON(jsonString) {
  // Escape HTML entities first
  let escaped = jsonString
    .replace(/&/g, '&')
    .replace(//g, '>');

  // Apply color classes via regex
  // Strings: "value"
  escaped = escaped.replace(
    /"([^"]*)"(?=:)/g,
    '<span class="json-key">"$1"</span>:'
  );

  // String values
  escaped = escaped.replace(
    /:"([^"]*)"/g,
    ': <span class="json-string">"$1"</span>'
  );

  // Numbers
  escaped = escaped.replace(
    /: (\d+\.?\d*)/g,
    ': <span class="json-number">$1</span>'
  );

  // Booleans and null
  escaped = escaped.replace(
    /: (true|false|null)/g,
    ': <span class="json-boolean">$1</span>'
  );

  return escaped;
}

// CSS for the highlight classes:
// .json-key { color: #8b5cf6; }
// .json-string { color: #22c55e; }
// .json-number { color: #3b82f6; }
// .json-boolean { color: #f59e0b; }

Regex-based highlighting is fast but not perfect. It won't handle nested objects inside strings, escaped quotes, or edge cases. For production use, consider a dedicated library. For a personal tool, this approach is more than adequate.

Step 6: Error Handling and UX

A JSON formatter is only useful if it tells users what went wrong when they paste invalid JSON. Here's the complete event handling:

JavaScript">// DOM references
const inputEl = document.getElementById('input');
const outputEl = document.getElementById('output');
const statusEl = document.getElementById('status');

// Format button handler
document.getElementById('format-btn').addEventListener('click', () => {
  const input = inputEl.value.trim();
  if (!input) {
    setStatus('Please enter some JSON', 'error');
    return;
  }

  const result = parseJSON(input);
  if (result.success) {
    outputEl.textContent = JSON.stringify(result.data, null, 2);
    setStatus('Valid JSON ✓', 'success');
  } else {
    outputEl.textContent = '';
    setStatus(`Error: ${result.error}`, 'error');
  }
});

// Minify button handler
document.getElementById('minify-btn').addEventListener('click', () => {
  const input = inputEl.value.trim();
  if (!input) {
    setStatus('Please enter some JSON', 'error');
    return;
  }

  const result = parseJSON(input);
  if (result.success) {
    outputEl.textContent = JSON.stringify(result.data);
    setStatus('Minified ✓', 'success');
  } else {
    outputEl.textContent = '';
    setStatus(`Error: ${result.error}`, 'error');
  }
});

// Copy button handler
document.getElementById('copy-btn').addEventListener('click', async () => {
  const text = outputEl.textContent;
  if (!text) {
    setStatus('Nothing to copy', 'warning');
    return;
  }

  try {
    await navigator.clipboard.writeText(text);
    setStatus('Copied to clipboard!', 'success');
  } catch (err) {
    setStatus('Failed to copy', 'error');
  }
});

// Clear button handler
document.getElementById('clear-btn').addEventListener('click', () => {
  inputEl.value = '';
  outputEl.textContent = '';
  setStatus('Cleared', 'info');
});

// Status helper
function setStatus(message, type = 'info') {
  statusEl.textContent = message;
  statusEl.className = `status status-${type}`;
}

Good UX in a JSON formatter means:

The Complete Solution

Here's everything assembled into a single, working HTML file:

index.html"><!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>JSON Formatter</title>
  <style>
    /* Paste the CSS here */
  </style>
</head>
<body>
  <div class="json-formatter">
    <!-- Paste the HTML from Step 1 here -->
  </div>
  <script>
    // Paste the JavaScript from Steps 2-6 here
  </script>
</body>
</html>

The full, working implementation is available as a single HTML file — no build step required. It includes:

Try It Live

The fully working JSON Formatter is live and free to use:

Open JSON Formatter → View This Tutorial

All tools on k1r4.space are free, open-source, and require no signup, tracking, or ads. They're built with vanilla HTML, CSS, and JavaScript — no frameworks, no build tools.

What We Learned

Building a JSON formatter taught us several important concepts: