Build a JSON Schema Validator Chrome Extension: Complete Developer's Guide

22 min read

Build a JSON Schema Validator Chrome Extension: Complete Developer's Guide

Build a JSON Schema Validator Chrome Extension: Complete Developer’s Guide

JSON Schema validation is an essential skill for modern web developers working with APIs, configuration files, and data exchange formats. Whether you’re building a REST API, consuming third-party web services, or designing configuration systems, having a reliable JSON Schema validator Chrome extension in your toolkit can dramatically improve your development workflow and catch errors before they reach production.

In this comprehensive guide, we’ll walk through the complete process of building a professional JSON Schema Validator Chrome extension from scratch. You’ll learn how to create a user-friendly interface, implement robust validation logic using the Ajv library, handle edge cases gracefully, and publish your extension to the Chrome Web Store. By the end of this tutorial, you’ll have a fully functional developer tool that you can use daily and share with the development community.


Why Build a JSON Schema Validator Extension?

Before diving into the code, let’s explore why building a JSON Schema validator Chrome extension is both a valuable learning exercise and a practical tool for your development workflow.

The Problem with JSON Validation

When working with JSON data—whether from API responses, configuration files, or data imports—developers often face the challenge of ensuring data conforms to expected structures. Manually checking each field is error-prone and time-consuming, especially as JSON objects grow in complexity. A single misplaced comma or wrong data type can cause applications to crash or behave unpredictably in production.

Traditional validation approaches include writing custom validation functions, using online validators, or relying on integrated development environment (IDE) extensions. However, having a browser-based solution offers unique advantages: you can validate JSON from any webpage, test API responses directly in the browser, and quickly prototype schema designs without leaving your development environment.

The Market Opportunity

Developer tools consistently rank among the most popular categories in the Chrome Web Store. Extensions that enhance productivity, improve code quality, or simplify common development tasks attract millions of users. A well-built JSON Schema validator can serve as the foundation for additional features like API testing, request building, and response formatting—each representing opportunities for future expansion.


Project Setup and Architecture

Let’s start building our JSON Schema validator Chrome extension. We’ll use modern web technologies and follow Chrome’s Manifest V3 guidelines for best performance and security.

Creating the Project Structure

First, create a new directory for your extension and set up the basic file structure:

mkdir json-schema-validator
cd json-schema-validator
mkdir -p icons popup js

This creates three key directories: icons for your extension icon, popup for the user interface, and js for your JavaScript logic. Keeping your code organized from the start makes maintenance easier as your extension grows.

The Manifest File

Every Chrome extension requires a manifest.json file that describes its configuration, permissions, and components. Create this file in your project root:

{
  "manifest_version": 3,
  "name": "JSON Schema Validator",
  "version": "1.0.0",
  "description": "Validate JSON data against JSON Schema with real-time feedback and detailed error reporting",
  "permissions": ["activeTab", "scripting"],
  "action": {
    "default_popup": "popup/popup.html",
    "default_icon": {
      "16": "icons/icon16.png",
      "48": "icons/icon48.png",
      "128": "icons/icon128.png"
    }
  },
  "icons": {
    "16": "icons/icon16.png",
    "48": "icons/icon48.png",
    "128": "icons/icon128.png"
  }
}

This Manifest V3 configuration declares the extension’s name, version, and purpose. The action key defines what happens when users click the extension icon—in our case, opening the popup interface. The permissions array includes activeTab and scripting, which we’ll use to extract JSON from web pages.


Building the User Interface

A good developer tool needs an intuitive interface that lets users focus on their work without unnecessary friction. Let’s create a clean, functional popup that makes JSON Schema validation straightforward.

The HTML Structure

Create popup/popup.html with the following structure:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>JSON Schema Validator</title>
  <link rel="stylesheet" href="popup.css">
</head>
<body>
  <div class="container">
    <header>
      <h1>JSON Schema Validator</h1>
      <p class="subtitle">Validate JSON against schemas instantly</p>
    </header>
    
    <main>
      <div class="input-section">
        <label for="json-input">JSON Input</label>
        <textarea id="json-input" placeholder="Paste your JSON here..."></textarea>
        <button id="validate-btn" class="primary-btn">Validate</button>
      </div>
      
      <div class="input-section">
        <label for="schema-input">JSON Schema</label>
        <textarea id="schema-input" placeholder="Paste your JSON Schema here..."></textarea>
      </div>
      
      <div class="results-section">
        <div id="status" class="status hidden"></div>
        <div id="errors" class="errors hidden"></div>
        <div id="success" class="success hidden">
          <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor">
            <path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"></path>
            <polyline points="22 4 12 14.01 9 11.01"></polyline>
          </svg>
          <span>Valid JSON!</span>
        </div>
      </div>
    </main>
    
    <footer>
      <button id="sample-btn" class="secondary-btn">Load Sample</button>
      <button id="clear-btn" class="secondary-btn">Clear All</button>
    </footer>
  </div>
  <script src="popup.js"></script>
</body>
</html>

This HTML provides two text areas for JSON input and schema definition, action buttons, and result display areas. The structure is semantically clear and accessibility-friendly, with proper labels and ARIA-ready markup.

Styling the Extension

Create popup/popup.css to give your extension a professional appearance:

* {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

body {
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
  width: 400px;
  min-height: 500px;
  background: #ffffff;
  color: #333;
}

.container {
  padding: 20px;
  display: flex;
  flex-direction: column;
  height: 100%;
}

header {
  text-align: center;
  margin-bottom: 20px;
  padding-bottom: 15px;
  border-bottom: 1px solid #e0e0e0;
}

h1 {
  font-size: 18px;
  font-weight: 600;
  color: #1a73e8;
}

.subtitle {
  font-size: 12px;
  color: #666;
  margin-top: 4px;
}

.input-section {
  margin-bottom: 15px;
}

label {
  display: block;
  font-size: 12px;
  font-weight: 500;
  color: #555;
  margin-bottom: 6px;
  text-transform: uppercase;
  letter-spacing: 0.5px;
}

textarea {
  width: 100%;
  height: 100px;
  padding: 10px;
  border: 1px solid #ddd;
  border-radius: 6px;
  font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
  font-size: 12px;
  resize: vertical;
  transition: border-color 0.2s;
}

textarea:focus {
  outline: none;
  border-color: #1a73e8;
}

textarea::placeholder {
  color: #aaa;
}

button {
  width: 100%;
  padding: 10px 16px;
  border: none;
  border-radius: 6px;
  font-size: 14px;
  font-weight: 500;
  cursor: pointer;
  transition: background-color 0.2s, transform 0.1s;
}

button:active {
  transform: scale(0.98);
}

.primary-btn {
  background: #1a73e8;
  color: white;
  margin-top: 10px;
}

.primary-btn:hover {
  background: #1557b0;
}

.secondary-btn {
  background: #f5f5f5;
  color: #333;
  flex: 1;
  margin: 0 4px;
}

.secondary-btn:hover {
  background: #e8e8e8;
}

footer {
  display: flex;
  gap: 8px;
  margin-top: auto;
  padding-top: 15px;
}

.results-section {
  margin-top: 15px;
  min-height: 60px;
}

.status {
  padding: 12px;
  border-radius: 6px;
  font-size: 13px;
  display: flex;
  align-items: center;
  gap: 8px;
}

.status.hidden {
  display: none;
}

.errors {
  background: #fef7f7;
  border: 1px solid #fecaca;
  border-radius: 6px;
  padding: 12px;
  font-size: 12px;
  max-height: 150px;
  overflow-y: auto;
}

.errors h3 {
  color: #dc2626;
  font-size: 13px;
  margin-bottom: 8px;
}

.errors ul {
  list-style: none;
}

.errors li {
  color: #991b1b;
  padding: 4px 0;
  font-family: monospace;
  word-break: break-all;
}

.success {
  background: #f0fdf4;
  border: 1px solid #bbf7d0;
  border-radius: 6px;
  padding: 15px;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  color: #166534;
  font-weight: 500;
}

.success svg {
  color: #22c55e;
}

.hidden {
  display: none !important;
}

This CSS creates a clean, modern interface that matches Chrome’s design language. The styling emphasizes usability with clear visual hierarchy, readable fonts, and helpful feedback states for validation results.


Implementing the Validation Logic

Now comes the core functionality: implementing JSON Schema validation in JavaScript. We’ll use Ajv (Another JSON Schema Validator), one of the fastest and most compliant JSON Schema validators available.

Setting Up Ajv

First, download the minified Ajv library and save it in your js folder. You can also use a CDN link in your popup. For simplicity, we’ll load it from a CDN in our JavaScript file.

The Main Logic

Create popup/popup.js with the complete validation implementation:

// JSON Schema Validator - Chrome Extension
// Uses Ajv for JSON Schema validation

document.addEventListener('DOMContentLoaded', () => {
  // DOM Elements
  const jsonInput = document.getElementById('json-input');
  const schemaInput = document.getElementById('schema-input');
  const validateBtn = document.getElementById('validate-btn');
  const sampleBtn = document.getElementById('sample-btn');
  const clearBtn = document.getElementById('clear-btn');
  const statusDiv = document.getElementById('status');
  const errorsDiv = document.getElementById('errors');
  const successDiv = document.getElementById('success');

  // Load Ajv library dynamically
  let ajv;
  
  async function loadAjv() {
    if (ajv) return ajv;
    
    return new Promise((resolve, reject) => {
      const script = document.createElement('script');
      script.src = 'https://cdnjs.cloudflare.com/ajax/libs/ajv/8.12.0/ajv7.min.js';
      script.onload = () => {
        window.ajv7().then(loadedAjv => {
          ajv = loadedAjv;
          resolve(ajv);
        });
      };
      script.onerror = reject;
      document.head.appendChild(script);
    });
  }

  // Validation function
  async function validateJSON() {
    // Clear previous results
    hideAllResults();
    
    const jsonText = jsonInput.value.trim();
    const schemaText = schemaInput.value.trim();
    
    // Check if inputs are empty
    if (!jsonText) {
      showError('Please enter JSON data to validate');
      return;
    }
    
    if (!schemaText) {
      showError('Please enter a JSON Schema');
      return;
    }
    
    try {
      // Parse JSON input
      let jsonData;
      try {
        jsonData = JSON.parse(jsonText);
      } catch (e) {
        showError(`Invalid JSON syntax: ${e.message}`);
        return;
      }
      
      // Parse schema
      let schema;
      try {
        schema = JSON.parse(schemaText);
      } catch (e) {
        showError(`Invalid Schema syntax: ${e.message}`);
        return;
      }
      
      // Load Ajv if not loaded
      await loadAjv();
      
      // Compile and validate
      const validate = ajv.compile(schema);
      const valid = validate(jsonData);
      
      if (valid) {
        showSuccess();
      } else {
        showValidationErrors(validate.errors);
      }
      
    } catch (error) {
      showError(`Validation error: ${error.message}`);
    }
  }

  // Display functions
  function hideAllResults() {
    statusDiv.classList.add('hidden');
    errorsDiv.classList.add('hidden');
    successDiv.classList.add('hidden');
  }
  
  function showError(message) {
    statusDiv.textContent = message;
    statusDiv.style.background = '#fef7f7';
    statusDiv.style.border = '1px solid #fecaca';
    statusDiv.style.color = '#dc2626';
    statusDiv.classList.remove('hidden');
    errorsDiv.classList.add('hidden');
    successDiv.classList.add('hidden');
  }
  
  function showValidationErrors(errors) {
    const errorsList = errorsDiv.querySelector('ul') || document.createElement('ul');
    errorsList.innerHTML = '';
    
    errors.forEach(error => {
      const li = document.createElement('li');
      const path = error.instancePath || 'root';
      li.textContent = `${path}: ${error.message} (${error.keyword})`;
      errorsList.appendChild(li);
    });
    
    if (!errorsDiv.querySelector('ul')) {
      const h3 = document.createElement('h3');
      h3.textContent = 'Validation Errors:';
      errorsDiv.insertBefore(h3, errorsList);
      errorsDiv.appendChild(errorsList);
    }
    
    errorsDiv.classList.remove('hidden');
    successDiv.classList.add('hidden');
  }
  
  function showSuccess() {
    successDiv.classList.remove('hidden');
  }

  // Sample data for demonstration
  const sampleJSON = {
    "name": "John Doe",
    "email": "john.doe@example.com",
    "age": 30,
    "website": "https://example.com"
  };
  
  const sampleSchema = {
    "type": "object",
    "properties": {
      "name": { "type": "string", "minLength": 1 },
      "email": { "type": "string", "format": "email" },
      "age": { "type": "integer", "minimum": 0 },
      "website": { "type": "string", "format": "uri" }
    },
    "required": ["name", "email"]
  };

  // Event listeners
  validateBtn.addEventListener('click', validateJSON);
  
  sampleBtn.addEventListener('click', () => {
    jsonInput.value = JSON.stringify(sampleJSON, null, 2);
    schemaInput.value = JSON.stringify(sampleSchema, null, 2);
    hideAllResults();
  });
  
  clearBtn.addEventListener('click', () => {
    jsonInput.value = '';
    schemaInput.value = '';
    hideAllResults();
  });
  
  // Allow Ctrl+Enter to validate
  [jsonInput, schemaInput].forEach(input => {
    input.addEventListener('keydown', (e) => {
      if (e.ctrlKey && e.key === 'Enter') {
        validateJSON();
      }
    });
  });
});

This JavaScript implements the core validation functionality with several important features:

  1. Dynamic Ajv Loading: The extension loads the Ajv library from a CDN when first needed, keeping the initial bundle size small.

  2. Comprehensive Error Handling: The code catches JSON parsing errors, schema parsing errors, and validation errors, displaying helpful messages to users.

  3. Detailed Error Reporting: When validation fails, users see exactly which fields failed and why, including the JSON path to each error.

  4. Keyboard Shortcuts: Users can press Ctrl+Enter to trigger validation quickly.

  5. Sample Data: The sample button loads example JSON and schema so users can test the extension immediately.


Adding Advanced Features

A basic validator is useful, but adding advanced features makes your extension stand out from the competition. Let’s explore some enhancements.

Extracting JSON from Webpages

Many developers want to validate JSON they’re viewing in their browser. Add this feature to your extension:

// Add to popup.js - Extract JSON from current tab
async function extractFromPage() {
  const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
  
  chrome.scripting.executeScript({
    target: { tabId: tab.id },
    function: () => {
      // Try to find JSON on the page
      const scripts = document.querySelectorAll('script[type="application/json"]');
      const preTags = document.querySelectorAll('pre');
      
      let jsonData = null;
      
      // Check script tags first
      scripts.forEach(script => {
        try {
          JSON.parse(script.textContent);
          jsonData = script.textContent;
        } catch (e) {}
      });
      
      // Check pre tags
      if (!jsonData) {
        preTags.forEach(pre => {
          try {
            const parsed = JSON.parse(pre.textContent);
            jsonData = pre.textContent;
          } catch (e) {}
        });
      }
      
      return jsonData;
    }
  }, (results) => {
    if (results && results[0]) {
      jsonInput.value = results[0];
    } else {
      showError('No JSON found on this page');
    }
  });
}

Schema Library

Allow users to save and reuse common schemas:

const savedSchemas = {
  'package.json': {
    "type": "object",
    "required": ["name", "version"],
    "properties": {
      "name": { "type": "string" },
      "version": { "type": "string" },
      "description": { "type": "string" }
    }
  }
};

Testing Your Extension

Before publishing, thoroughly test your extension to ensure it works correctly across different scenarios.

Loading the Extension

To test your extension in Chrome:

  1. Navigate to chrome://extensions/
  2. Enable “Developer mode” in the top right corner
  3. Click “Load unpacked” and select your extension directory
  4. The extension icon should appear in your toolbar

Test Cases

Test these scenarios:

Test Case Input JSON Schema Expected Result
Valid simple object {"name": "test"} Basic object schema Success
Missing required field {"age": 25} Required “name” field Error: missing property
Wrong type {"age": "twenty"} Age as integer Error: wrong type
Valid array [1, 2, 3] Array of integers Success
Invalid format {"email": "not-an-email"} Email format Error: invalid format

Publishing to the Chrome Web Store

Once your extension is tested and polished, it’s time to share it with the world.

Prepare for Publishing

  1. Create icons: Generate 16x16, 48x48, and 128x128 PNG icons
  2. Take screenshots: Create 1280x800 PNG screenshots of your extension
  3. Write a compelling description: Highlight key features and benefits
  4. Set a category: Choose “Developer Tools” for best visibility

Upload Process

  1. Package your extension as a ZIP file
  2. Go to the Chrome Web Store Developer Dashboard
  3. Create a new item and upload your ZIP
  4. Fill in the store listing details
  5. Submit for review

Conclusion

Congratulations! You’ve built a complete JSON Schema validator Chrome extension from scratch. This extension demonstrates essential Chrome extension development concepts including Manifest V3 configuration, popup interfaces, content scripts, and integration with external libraries.

The JSON Schema validator you created is a valuable tool for any developer working with JSON APIs, configuration files, or data validation. It showcases your ability to build practical developer tools and provides a solid foundation for future extensions.

Remember to continue improving your extension based on user feedback, add new features like saved schemas or API testing capabilities, and engage with the developer community. With this project, you’ve demonstrated practical skills in Chrome extension development that are highly valued in the industry.


Additional Resources

Start building your JSON Schema validator today and transform the way you validate JSON data in your development workflow!

No previous article
No next article