How to Build a Responsive Email Preview Chrome Extension

18 min read

How to Build a Responsive Email Preview Chrome Extension

How to Build a Responsive Email Preview Chrome Extension

Email development remains one of the most challenging disciplines in web development. Unlike modern web pages that render consistently across browsers, emails must contend with a fragmented landscape of email clients, each with its own rendering engine, CSS limitations, and quirky behavior. For developers and marketers who send emails campaigns, testing across multiple devices and clients is not just recommended—it is essential. Building a responsive email preview Chrome extension can dramatically streamline this workflow, giving you instant feedback on how your emails appear across different screen sizes and email clients without leaving your browser.

In this comprehensive guide, we will walk through the complete process of building an email tester Chrome extension from the ground up. Whether you are a seasoned Chrome extension developer or just getting started with browser extension development, this tutorial will provide you with the knowledge and practical code examples needed to create a professional-grade email preview tool.

Why Build an Email Preview Extension?

Before diving into the code, let us understand why building an email preview extension is a valuable project. The email development workflow typically involves switching between multiple tools, manually resizing browser windows, and using external services to test email rendering across different clients. A well-designed responsive email chrome extension can consolidate many of these tasks into a single, seamless experience.

The market demand for email testing tools is substantial. Developers and marketers spend significant time and resources ensuring their emails render correctly across the dozens of popular email clients—including Gmail, Outlook, Apple Mail, Yahoo Mail, and mobile email apps. By building an extension that provides instant previews and testing capabilities, you are solving a real pain point that affects millions of professionals worldwide.

Additionally, Chrome extensions have unique advantages over standalone web applications. They can access browser context, integrate with developer tools, and be installed with a single click. This makes an email preview extension particularly powerful for developers who spend most of their day in the browser.

Project Planning and Architecture

Every successful Chrome extension begins with careful planning. For a responsive email preview extension, you need to consider several key features that will make your tool useful:

  • Live preview panel that displays the email in different viewport sizes
  • Device simulation for common screen sizes (mobile, tablet, desktop)
  • Email client simulation to approximate how different clients render emails
  • Code editor integration for composing and editing email HTML
  • Screenshot capability to capture previews for sharing
  • Responsive testing to verify emails adapt correctly across screen widths

For this tutorial, we will focus on the core functionality: providing responsive previews and device simulation. This gives us a solid foundation that can be extended with additional features later.

The architecture of our extension will follow the standard Chrome extension pattern with a popup interface, content scripts for injection, and background scripts for persistent state management. We will use modern JavaScript (ES6+) and avoid heavy dependencies to keep the extension lightweight and fast.

Setting Up the Project Structure

Chrome extensions require a specific directory structure and configuration files. Let us set up our project:

email-preview-extension/
├── manifest.json
├── popup.html
├── popup.js
├── popup.css
├── content.js
├── background.js
├── preview.html
├── preview.js
├── preview.css
└── icons/
    ├── icon16.png
    ├── icon48.png
    └── icon128.png

The manifest.json file is the heart of any Chrome extension. It defines the extension’s permissions, resources, and entry points. Here is our manifest configuration:

{
  "manifest_version": 3,
  "name": "Responsive Email Preview",
  "version": "1.0",
  "description": "Preview and test responsive emails across multiple devices and screen sizes",
  "permissions": [
    "activeTab",
    "scripting",
    "storage"
  ],
  "action": {
    "default_popup": "popup.html",
    "default_icon": {
      "16": "icons/icon16.png",
      "48": "icons/icon48.png",
      "128": "icons/icon128.png"
    }
  },
  "background": {
    "service_worker": "background.js"
  },
  "icons": {
    "16": "icons/icon16.png",
    "48": "icons/icon48.png",
    "128": "icons/icon128.png"
  }
}

This manifest requests the minimum permissions needed for our extension. We use activeTab to access the current tab’s content, scripting to inject our preview code, and storage to save user preferences like default viewport sizes.

Building the Popup Interface

The popup is what users see when they click the extension icon. This is where users will input their email HTML and select preview options. Let us create a clean, functional popup interface:

<!DOCTYPE html>
<html>
<head>
  <link rel="stylesheet" href="popup.css">
</head>
<body>
  <div class="container">
    <header>
      <h1>Email Preview</h1>
    </header>
    
    <section class="input-section">
      <label for="email-html">Paste your email HTML:</label>
      <textarea id="email-html" placeholder="<!DOCTYPE html><html>...</textarea>
    </section>
    
    <section class="device-selector">
      <h3>Preview Size</h3>
      <div class="device-buttons">
        <button data-width="320" data-height="568" class="device-btn">
          <span class="device-icon">📱</span>
          <span>Mobile</span>
        </button>
        <button data-width="375" data-height="667" class="device-btn">
          <span class="device-icon">📱</span>
          <span>iPhone</span>
        </button>
        <button data-width="768" data-height="1024" class="device-btn">
          <span class="device-icon">📱</span>
          <span>Tablet</span>
        </button>
        <button data-width="1024" data-height="768" class="device-btn">
          <span class="device-icon">💻</span>
          <span>Desktop</span>
        </button>
        <button data-width="1440" data-height="900" class="device-btn">
          <span class="device-icon">🖥️</span>
          <span>Large</span>
        </button>
      </div>
    </section>
    
    <section class="actions">
      <button id="preview-btn" class="primary-btn">Open Preview</button>
      <button id="clear-btn" class="secondary-btn">Clear</button>
    </section>
  </div>
  
  <script src="popup.js"></script>
</body>
</html>

The popup includes a textarea for pasting email HTML and quick-select buttons for common device sizes. This design keeps the interface clean while providing quick access to the most common testing scenarios.

Implementing the Preview Functionality

The real power of our extension lies in the preview functionality. When users click “Open Preview,” we will create a new tab with our preview page that renders the email HTML at the selected viewport size. Here is how we implement this in popup.js:

document.addEventListener('DOMContentLoaded', () => {
  const emailHtmlInput = document.getElementById('email-html');
  const previewBtn = document.getElementById('preview-btn');
  const clearBtn = document.getElementById('clear-btn');
  const deviceButtons = document.querySelectorAll('.device-btn');
  
  let selectedWidth = 375;
  let selectedHeight = 667;
  
  // Load saved HTML from storage
  chrome.storage.local.get(['emailHtml'], (result) => {
    if (result.emailHtml) {
      emailHtmlInput.value = result.emailHtml;
    }
  });
  
  // Device button click handlers
  deviceButtons.forEach(btn => {
    btn.addEventListener('click', () => {
      deviceButtons.forEach(b => b.classList.remove('active'));
      btn.classList.add('active');
      selectedWidth = parseInt(btn.dataset.width);
      selectedHeight = parseInt(btn.dataset.height);
    });
  });
  
  // Set default active button
  deviceButtons[1].classList.add('active');
  
  // Preview button handler
  previewBtn.addEventListener('click', () => {
    const emailHtml = emailHtmlInput.value.trim();
    
    if (!emailHtml) {
      alert('Please enter some email HTML to preview');
      return;
    }
    
    // Save to storage for persistence
    chrome.storage.local.set({ emailHtml, selectedWidth, selectedHeight });
    
    // Create preview page URL with parameters
    const previewUrl = chrome.runtime.getURL('preview.html') + 
      `?html=${encodeURIComponent(emailHtml)}` +
      `&width=${selectedWidth}` +
      `&height=${selectedHeight}`;
    
    // Open preview in new tab
    chrome.tabs.create({ url: previewUrl });
  });
  
  // Clear button handler
  clearBtn.addEventListener('click', () => {
    emailHtmlInput.value = '';
    chrome.storage.local.set({ emailHtml: '' });
  });
});

This code handles saving and loading user preferences, managing device selection, and opening the preview page. We use Chrome’s storage API to persist the email HTML so users do not lose their work if they accidentally close the popup.

Creating the Preview Page

The preview page is where users actually see their email rendered. It receives the email HTML as a URL parameter and renders it inside a container that simulates different device sizes. Here is the preview.html:

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>Email Preview</title>
  <link rel="stylesheet" href="preview.css">
</head>
<body>
  <div class="preview-container">
    <div class="toolbar">
      <div class="viewport-info">
        <span id="viewport-size">375 × 667</span>
      </div>
      <div class="toolbar-actions">
        <button id="toggle-orientation" class="toolbar-btn">Rotate</button>
        <button id="refresh-btn" class="toolbar-btn">Refresh</button>
      </div>
    </div>
    
    <div class="preview-frame-wrapper">
      <iframe id="preview-frame" sandbox="allow-same-origin"></iframe>
    </div>
  </div>
  
  <script src="preview.js"></script>
</body>
</html>

The preview.js script extracts the HTML and dimensions from the URL parameters and renders the email inside an iframe. This approach isolates the email styles from our extension interface:

document.addEventListener('DOMContentLoaded', () => {
  const params = new URLSearchParams(window.location.search);
  const html = decodeURIComponent(params.get('html') || '');
  let width = parseInt(params.get('width') || '375');
  let height = parseInt(params.get('height') || '667');
  
  const frame = document.getElementById('preview-frame');
  const viewportInfo = document.getElementById('viewport-size');
  const orientationBtn = document.getElementById('toggle-orientation');
  const refreshBtn = document.getElementById('refresh-btn');
  
  function updatePreview() {
    const wrapper = document.querySelector('.preview-frame-wrapper');
    wrapper.style.width = width + 'px';
    wrapper.style.height = height + 'px';
    viewportInfo.textContent = `${width} × ${height}`;
    
    // Write HTML to iframe
    const doc = frame.contentWindow.document;
    doc.open();
    doc.write(html);
    doc.close();
  }
  
  // Toggle landscape/portrait
  orientationBtn.addEventListener('click', () => {
    [width, height] = [height, width];
    updatePreview();
  });
  
  // Refresh preview
  refreshBtn.addEventListener('click', updatePreview);
  
  // Initial render
  updatePreview();
});

The iframe approach provides realistic rendering while keeping the email contained within our controlled dimensions. Users can rotate between portrait and landscape views and refresh to see their latest changes.

Styling the Extension

A polished user interface makes your extension more enjoyable to use. Here is the CSS for our popup:

* {
  box-sizing: border-box;
}

body {
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  width: 360px;
  margin: 0;
  padding: 0;
  background: #f5f5f5;
}

.container {
  padding: 16px;
}

header h1 {
  font-size: 18px;
  margin: 0 0 16px 0;
  color: #333;
}

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

.input-section label {
  display: block;
  font-size: 13px;
  font-weight: 600;
  margin-bottom: 6px;
  color: #555;
}

#email-html {
  width: 100%;
  height: 120px;
  padding: 10px;
  border: 1px solid #ddd;
  border-radius: 6px;
  font-family: monospace;
  font-size: 12px;
  resize: vertical;
}

#email-html:focus {
  outline: none;
  border-color: #4285f4;
}

.device-selector h3 {
  font-size: 13px;
  font-weight: 600;
  margin: 0 0 10px 0;
  color: #555;
}

.device-buttons {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 8px;
}

.device-btn {
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 10px 8px;
  background: white;
  border: 1px solid #ddd;
  border-radius: 6px;
  cursor: pointer;
  transition: all 0.2s;
}

.device-btn:hover {
  border-color: #4285f4;
}

.device-btn.active {
  background: #e8f0fe;
  border-color: #4285f4;
}

.device-icon {
  font-size: 20px;
  margin-bottom: 4px;
}

.device-btn span:last-child {
  font-size: 11px;
  color: #666;
}

.actions {
  display: flex;
  gap: 8px;
  margin-top: 16px;
}

.primary-btn, .secondary-btn {
  flex: 1;
  padding: 10px;
  border: none;
  border-radius: 6px;
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
}

.primary-btn {
  background: #4285f4;
  color: white;
}

.primary-btn:hover {
  background: #3367d6;
}

.secondary-btn {
  background: #e8e8e8;
  color: #333;
}

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

Adding Content Script Functionality

For more advanced features, we can add a content script that allows users to preview emails directly on any webpage. This is particularly useful when viewing email templates in a code repository or email marketing platform. Here is how we can implement this:

// content.js - Run on user-activated pages
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
  if (message.action === 'previewEmail') {
    const { html, width, height } = message;
    
    // Create preview overlay
    const overlay = document.createElement('div');
    overlay.id = 'email-preview-overlay';
    overlay.innerHTML = `
      <div class="preview-header">
        <span>Email Preview - ${width}×${height}</span>
        <button id="close-preview">×</button>
      </div>
      <div class="preview-container" style="width:${width}px;height:${height}px;">
        <iframe sandbox="allow-same-origin"></iframe>
      </div>
    `;
    
    document.body.appendChild(overlay);
    
    // Render email in iframe
    const iframe = overlay.querySelector('iframe');
    const doc = iframe.contentWindow.document;
    doc.open();
    doc.write(html);
    doc.close();
    
    // Close handler
    document.getElementById('close-preview').addEventListener('click', () => {
      overlay.remove();
    });
    
    sendResponse({ success: true });
  }
  
  return true;
});

This content script listens for messages from our popup and creates an overlay preview directly on the current page. It gives users maximum flexibility in how they test their emails.

Best Practices for Email Extension Development

When building an email preview extension, there are several best practices you should follow to ensure a quality product:

First, always assume that email clients have limited CSS support. Your extension should help developers identify potential issues with their email HTML. Consider adding warnings for commonly unsupported properties like flexbox, grid, or modern CSS functions.

Second, test your extension thoroughly across different Chrome versions and on different operating systems. Browser behavior can vary, and your extension should work consistently.

Third, consider adding features that help with common email development challenges. This includes previews for dark mode, testing email subject lines and preheader text, and integration with popular email marketing platforms.

Fourth, optimize your extension’s performance. Email HTML can be complex, and rendering previews should be fast. Use efficient DOM manipulation and avoid unnecessary reflows.

Finally, gather user feedback and iterate on your extension. The best tools are built by developers who understand the real-world challenges of their users.

Extending Your Email Tester

The foundation we have built here can be extended with many powerful features:

  • Email client simulation that approximates how Gmail, Outlook, and Apple Mail render emails
  • Screenshot capture for sharing previews with team members
  • HTML validation to catch common email development errors
  • Template library for common email layouts
  • Integration with MJML or other email frameworks
  • Dark mode preview to test email appearance in dark themes
  • Accessibility checking for email content

Each of these features would make your extension more valuable to users and differentiate it from existing tools in the market.

Conclusion

Building a responsive email preview Chrome extension is a rewarding project that solves real problems for developers and marketers. In this guide, we have covered the complete development process from project planning to implementation, providing you with working code that you can extend and customize.

The extension we built provides essential functionality for email testing: viewport simulation, device presets, and a clean preview interface. With the solid foundation established here, you can continue adding features to create a professional-grade email tester that serves the email development community.

Remember to test your extension thoroughly, gather user feedback, and iterate on the design. The email development ecosystem is always evolving, and a well-maintained extension can become an indispensable tool for thousands of developers worldwide.

Start building today, and transform the way you test responsive emails in Chrome.

No previous article
No next article