Build a Cookie Editor Chrome Extension

23 min read

Build a Cookie Editor Chrome Extension

Build a Cookie Editor Chrome Extension

Building a cookie editor extension is one of the most practical projects you can undertake as a Chrome extension developer. Cookie editor extensions are incredibly useful tools that allow users to view, edit, delete, and manage browser cookies for any website they visit. Whether you need to debug web applications, test session handling, manage multiple accounts, or simply understand how websites track you, a well-built cookie editor extension provides all these capabilities at your fingertips.

In this comprehensive guide, we’ll walk through the complete process of building a production-ready Cookie Editor Chrome Extension using Manifest V3. You’ll learn about the Chrome Cookies API, user interface design, security best practices, and advanced features that will make your extension stand out from the competition.


Before we dive into the technical details, let’s explore why cookie editor extensions are valuable and in high demand. Understanding the purpose and use cases will help you design a better product.

Real-World Use Cases

Cookie editor extensions serve multiple important purposes in the web development and browsing ecosystem. Web developers use them extensively for debugging session-related issues, testing authentication flows, and inspecting how websites store user data. QA engineers rely on cookie editors to verify cookie-based features, test cookie expiration behaviors, and validate session management.

Beyond development, regular users benefit from cookie editors for managing their online privacy, clearing unwanted tracking cookies, managing multiple accounts on the same website, and bypassing certain cookie-based restrictions. The versatility of these tools makes them one of the most popular categories of browser extensions.

Market Demand and Competition

The Chrome Web Store has several established cookie editor extensions, but there’s always room for improvement. Users frequently complain about existing tools being outdated, lacking modern features, or having cluttered interfaces. By building a cookie editor with a clean design, fast performance, and additional features like bulk operations, import/export functionality, and advanced filtering, you can differentiate your extension and attract a loyal user base.


Project Setup and Manifest Configuration

Every Chrome extension begins with a properly configured manifest.json file. This file tells Chrome about your extension’s permissions, capabilities, and the files it comprises.

Creating the Manifest

For our cookie editor extension, we need to declare the cookies permission and specify the extension’s background service worker and popup interface. Here’s the manifest.json configuration:

{
  "manifest_version": 3,
  "name": "Cookie Editor Pro",
  "version": "1.0.0",
  "description": "A powerful cookie editor to view, edit, and manage cookies for any website",
  "permissions": [
    "cookies",
    "storage",
    "tabs",
    "activeTab"
  ],
  "host_permissions": [
    "<all_urls>"
  ],
  "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"
  }
}

The manifest declares several critical permissions. The “cookies” permission is essential for reading and modifying cookies, while “tabs” and “activeTab” help us identify which website the user is currently viewing. The “storage” permission enables us to save user preferences and recent operations. The host permissions with “" allow the extension to work on any website, which is necessary for a universal cookie editor.

Extension File Structure

Organize your project with a clear folder structure that separates concerns and makes maintenance easier:

cookie-editor-extension/
├── manifest.json
├── popup.html
├── popup.js
├── popup.css
├── background.js
├── icons/
│   ├── icon16.png
│   ├── icon48.png
│   └── icon128.png
└── utils/
    ├── cookieParser.js
    └── storageManager.js

This structure separates the popup interface (HTML, CSS, JavaScript), background service worker, utility functions, and icon assets. Keeping concerns separated makes the codebase more maintainable and easier to extend with new features later.


The Chrome Cookies API

The Chrome Cookies API is the foundation of our extension. This API provides comprehensive methods for querying, setting, and removing cookies. Understanding its capabilities is essential for building a robust cookie editor.

Core API Methods

The Chrome Cookies API offers several key methods that we’ll use extensively in our extension. The chrome.cookies.get() method retrieves a specific cookie by name and URL. The chrome.cookies.getAll() method retrieves all cookies matching specified parameters, which is the primary method for listing cookies for a particular domain. The chrome.cookies.set() method creates or updates a cookie with specified properties, while chrome.cookies.remove() deletes a specific cookie.

Additionally, the chrome.cookies.onChanged event listener allows our extension to monitor cookie changes in real-time, enabling features like automatic refresh when cookies are modified externally.

Each cookie in Chrome’s API is represented as an object with numerous properties. The essential properties include the cookie name, value, domain, path, expiration date, secure flag, httpOnly flag, sameSite attribute, and session status. When building our editor, we need to display and allow editing of these properties appropriately.

Here’s an example of how to retrieve all cookies for the current tab:

async function getCookiesForCurrentTab() {
  const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
  const url = new URL(tab.url);
  
  const cookies = await chrome.cookies.getAll({
    domain: url.hostname
  });
  
  return cookies;
}

This function first queries for the active tab, extracts the hostname from its URL, then retrieves all cookies matching that domain. The resulting array contains cookie objects with all their properties.


The popup interface is what users interact with most frequently. It should be intuitive, fast, and provide all the functionality users expect from a cookie editor.

HTML Structure

The popup HTML should include a header with basic information, a search/filter bar, a cookie list container, and action buttons for common operations. Here’s a practical structure:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Cookie Editor</title>
  <link rel="stylesheet" href="popup.css">
</head>
<body>
  <div class="container">
    <header class="header">
      <h1>🍪 Cookie Editor</h1>
      <span class="domain-display" id="currentDomain">Loading...</span>
    </header>
    
    <div class="toolbar">
      <input type="text" id="searchInput" placeholder="Search cookies...">
      <button id="refreshBtn" title="Refresh"></button>
      <button id="addCookieBtn" title="Add Cookie">+</button>
    </div>
    
    <div class="cookie-list" id="cookieList">
      <div class="loading">Loading cookies...</div>
    </div>
    
    <footer class="footer">
      <button id="exportBtn" class="secondary">Export</button>
      <button id="importBtn" class="secondary">Import</button>
      <button id="clearAllBtn" class="danger">Clear All</button>
    </footer>
  </div>
  
  <script src="popup.js"></script>
</body>
</html>

This structure provides a clean, organized interface with clear sections for domain information, search functionality, the cookie list, and action buttons. The design prioritizes usability while keeping the interface compact enough to fit comfortably in a browser popup.

Styling with CSS

The CSS should create a polished, professional appearance while ensuring the interface remains functional and responsive. Consider using a modern color scheme, clear typography, and appropriate spacing:

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

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

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

.header {
  background: #4285f4;
  color: white;
  padding: 12px 16px;
}

.header h1 {
  font-size: 16px;
  font-weight: 600;
  margin-bottom: 4px;
}

.domain-display {
  font-size: 12px;
  opacity: 0.9;
}

.toolbar {
  display: flex;
  gap: 8px;
  padding: 12px;
  background: white;
  border-bottom: 1px solid #e0e0e0;
}

.toolbar input {
  flex: 1;
  padding: 8px 12px;
  border: 1px solid #ddd;
  border-radius: 4px;
  font-size: 14px;
}

.toolbar button {
  width: 36px;
  height: 36px;
  border: none;
  border-radius: 4px;
  background: #f0f0f0;
  cursor: pointer;
  font-size: 16px;
}

.toolbar button:hover {
  background: #e0e0e0;
}

.cookie-list {
  flex: 1;
  overflow-y: auto;
  padding: 8px;
  background: #fafafa;
}

This CSS creates a professional-looking popup with proper spacing, colors, and interactive states. The layout uses flexbox for flexibility and includes appropriate hover states for buttons.


Implementing Core Functionality

Now let’s implement the JavaScript logic that powers our cookie editor. This is where the real functionality comes to life.

Loading and Displaying Cookies

The core of our extension involves retrieving cookies and displaying them in a user-friendly format:

document.addEventListener('DOMContentLoaded', async () => {
  await loadCookies();
  setupEventListeners();
});

async function loadCookies() {
  const cookieList = document.getElementById('cookieList');
  const domainDisplay = document.getElementById('currentDomain');
  
  try {
    const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
    const url = new URL(tab.url);
    domainDisplay.textContent = url.hostname;
    
    const cookies = await chrome.cookies.getAll({ domain: url.hostname });
    
    if (cookies.length === 0) {
      cookieList.innerHTML = '<div class="empty">No cookies found for this domain</div>';
      return;
    }
    
    cookieList.innerHTML = cookies.map(cookie => createCookieItem(cookie)).join('');
    setupCookieEventListeners();
    
  } catch (error) {
    cookieList.innerHTML = `<div class="error">Error loading cookies: ${error.message}</div>`;
  }
}

function createCookieItem(cookie) {
  const isSecure = cookie.secure ? '🔒' : '';
  const isHttpOnly = cookie.httpOnly ? '💻' : '';
  
  return `
    <div class="cookie-item" data-name="${encodeURIComponent(cookie.name)}">
      <div class="cookie-header">
        <span class="cookie-name">${escapeHtml(cookie.name)}</span>
        <div class="cookie-flags">${isSecure} ${isHttpOnly}</div>
      </div>
      <div class="cookie-value">${escapeHtml(cookie.value)}</div>
      <div class="cookie-meta">
        <span>Path: ${cookie.path}</span>
        <span>Expires: ${formatExpiration(cookie.expirationDate)}</span>
      </div>
      <div class="cookie-actions">
        <button class="edit-btn" data-cookie="${encodeURIComponent(JSON.stringify(cookie))}">Edit</button>
        <button class="delete-btn" data-name="${encodeURIComponent(cookie.name)}" data-domain="${encodeURIComponent(cookie.domain)}" data-path="${encodeURIComponent(cookie.path)}">Delete</button>
      </div>
    </div>
  `;
}

function escapeHtml(text) {
  const div = document.createElement('div');
  div.textContent = text;
  return div.innerHTML;
}

function formatExpiration(timestamp) {
  if (!timestamp) return 'Session';
  return new Date(timestamp * 1000).toLocaleString();
}

This code retrieves cookies for the current tab, displays them in individual cards, and includes essential information like the cookie name, value, path, expiration, and flags. Each cookie item includes edit and delete buttons for quick actions.

Editing Cookies

Editing cookies requires careful handling since we need to preserve properties we don’t modify while updating the ones the user changes:

async function editCookie(originalCookie, updates) {
  const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
  const url = new URL(tab.url);
  
  const updatedCookie = {
    url: url.origin,
    name: originalCookie.name,
    value: updates.value || originalCookie.value,
    domain: updates.domain || originalCookie.domain,
    path: updates.path || originalCookie.path,
    secure: updates.secure !== undefined ? updates.secure : originalCookie.secure,
    httpOnly: updates.httpOnly !== undefined ? updates.httpOnly : originalCookie.httpOnly,
    sameSite: updates.sameSite || originalCookie.sameSite
  };
  
  if (updates.expirationDate) {
    updatedCookie.expirationDate = updates.expirationDate;
  }
  
  try {
    await chrome.cookies.remove({
      url: url.origin,
      name: originalCookie.name,
      domain: originalCookie.domain,
      path: originalCookie.path
    });
    
    await chrome.cookies.set(updatedCookie);
    await loadCookies();
    
    return { success: true };
  } catch (error) {
    return { success: false, error: error.message };
  }
}

The editing process involves removing the original cookie and creating a new one with the updated properties. This is necessary because Chrome’s API doesn’t support in-place modification of cookies.

Deleting Cookies

Deleting cookies is straightforward but requires matching the exact URL, domain, and path that were used when the cookie was set:

async function deleteCookie(name, domain, path) {
  const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
  const url = new URL(tab.url);
  
  const cookieDetails = {
    url: url.origin,
    name: name,
    domain: domain,
    path: path
  };
  
  try {
    await chrome.cookies.remove(cookieDetails);
    await loadCookies();
    return { success: true };
  } catch (error) {
    return { success: false, error: error.message };
  }
}

function setupCookieEventListeners() {
  document.querySelectorAll('.delete-btn').forEach(btn => {
    btn.addEventListener('click', async (e) => {
      const name = decodeURIComponent(e.target.dataset.name);
      const domain = decodeURIComponent(e.target.dataset.domain);
      const path = decodeURIComponent(e.target.dataset.path);
      
      if (confirm(`Delete cookie "${name}"?`)) {
        await deleteCookie(name, domain, path);
      }
    });
  });
  
  document.querySelectorAll('.edit-btn').forEach(btn => {
    btn.addEventListener('click', (e) => {
      const cookie = JSON.parse(decodeURIComponent(e.target.dataset.cookie));
      showEditModal(cookie);
    });
  });
}

These functions handle the deletion of cookies with proper confirmation and refresh the list after successful deletion.


Advanced Features

To make your cookie editor stand out from competitors, consider implementing these advanced features that provide additional value to users.

Allowing users to export their cookies for backup or import cookies from other sources is incredibly useful for developers and power users:

async function exportCookies() {
  const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
  const url = new URL(tab.url);
  
  const cookies = await chrome.cookies.getAll({ domain: url.hostname });
  
  const exportData = {
    domain: url.hostname,
    timestamp: new Date().toISOString(),
    cookies: cookies
  };
  
  const blob = new Blob([JSON.stringify(exportData, null, 2)], { type: 'application/json' });
  const downloadUrl = URL.createObjectURL(blob);
  
  const a = document.createElement('a');
  a.href = downloadUrl;
  a.download = `cookies-${url.hostname}-${Date.now()}.json`;
  a.click();
  
  URL.revokeObjectURL(downloadUrl);
}

async function importCookies(jsonData) {
  try {
    const data = JSON.parse(jsonData);
    const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
    const url = new URL(tab.url);
    
    for (const cookie of data.cookies) {
      const newCookie = {
        url: url.origin,
        name: cookie.name,
        value: cookie.value,
        domain: cookie.domain,
        path: cookie.path,
        secure: cookie.secure,
        httpOnly: cookie.httpOnly,
        sameSite: cookie.sameSite
      };
      
      if (cookie.expirationDate) {
        newCookie.expirationDate = cookie.expirationDate;
      }
      
      await chrome.cookies.set(newCookie);
    }
    
    await loadCookies();
    return { success: true };
  } catch (error) {
    return { success: false, error: error.message };
  }
}

Adding search functionality helps users find specific cookies among potentially hundreds of cookies on a domain:

function setupSearch() {
  const searchInput = document.getElementById('searchInput');
  
  searchInput.addEventListener('input', (e) => {
    const searchTerm = e.target.value.toLowerCase();
    const cookieItems = document.querySelectorAll('.cookie-item');
    
    cookieItems.forEach(item => {
      const name = item.querySelector('.cookie-name').textContent.toLowerCase();
      const value = item.querySelector('.cookie-value').textContent.toLowerCase();
      
      if (name.includes(searchTerm) || value.includes(searchTerm)) {
        item.style.display = 'block';
      } else {
        item.style.display = 'none';
      }
    });
  });
}

Using the chrome.cookies.onChanged event, you can implement real-time monitoring that updates the UI whenever cookies change:

chrome.cookies.onChanged.addListener((changeInfo) => {
  const { removed, cookie, cause } = changeInfo;
  
  console.log(`Cookie ${removed ? 'removed' : 'changed'}: ${cookie.name}`);
  
  // Refresh the cookie list when changes occur
  loadCookies();
  
  // Optionally show a notification
  showNotification(
    removed ? 'Cookie deleted' : 'Cookie updated', 
    `${cookie.name} on ${cookie.domain}`
  );
});

This feature is particularly useful for developers who need to see immediate feedback when their applications modify cookies.


Security Considerations

When building a cookie editor, security should be at the forefront of your design decisions. Cookie editors have access to sensitive data, and users trust you to handle their information responsibly.

Validate All Input

Never trust user input or cookie data from websites. Always validate and sanitize any data before displaying it or using it in API calls:

```javascriptfunction sanitizeCookieData(data) { return { name: String(data.name).substring(0, 500), value: String(data.value).substring(0, 10000), domain: validateDomain(data.domain), path: String(data.path).substring(0, 500) }; }

function validateDomain(domain) { const allowedDomains = [‘localhost’, ‘127.0.0.1’]; const domainRegex = /^(a-zA-Z0-9?.)+[a-zA-Z]{2,}$/;

if (allowedDomains.includes(domain) || domainRegex.test(domain)) { return domain; }

throw new Error(‘Invalid domain’); }


### Handle Sensitive Data Carefully

Avoid logging or displaying sensitive cookie values unnecessarily. Consider adding a feature that masks sensitive values by default:

```javascriptfunction shouldMaskValue(cookieName) {
  const sensitivePatterns = [
    /token/i,
    /session/i,
    /auth/i,
    /password/i,
    /secret/i,
    /key/i
  ];
  
  return sensitivePatterns.some(pattern => pattern.test(cookieName));
}

Implement Proper Error Handling

Always wrap API calls in try-catch blocks and provide meaningful error messages to users:

async function safeCookieOperation(operation) {
  try {
    return await operation();
  } catch (error) {
    console.error('Cookie operation failed:', error);
    showError(`Operation failed: ${error.message}`);
    return null;
  }
}

Testing Your Extension

Thorough testing is essential for a cookie editor extension since it deals with sensitive browser data and needs to work correctly across different websites.

Manual Testing Checklist

Test your extension on various types of websites to ensure compatibility. Test with websites that have many cookies and few cookies, secure (HTTPS) and HTTP websites, websites with various cookie attributes like HttpOnly and SameSite, the import and export functionality, the edit and delete operations, and the real-time monitoring feature.

Using Chrome’s Extension Testing Features

Chrome provides excellent developer tools for testing extensions. You can load your extension in development mode, inspect background service workers, view console logs, and use the Chrome Cookies API directly from the developer console to verify your extension’s behavior.


Publishing Your Extension

Once your extension is tested and ready, you can publish it to the Chrome Web Store. The publishing process involves creating a developer account, preparing promotional assets, and following Chrome’s policies.

Pre-submission Checklist

Before submitting, ensure your extension has a clear and descriptive name, informative description and screenshots, a privacy policy if your extension handles user data, proper icon assets in all required sizes, and compliance with Chrome’s extension policies.


Conclusion

Building a Cookie Editor Chrome Extension is an excellent project that teaches you valuable skills while creating a genuinely useful tool. You’ve learned about the Chrome Cookies API, Manifest V3 configuration, popup interface design, cookie manipulation, security best practices, and the complete development workflow from concept to publication.

The cookie editor you built in this guide includes essential features like viewing, editing, and deleting cookies, search and filter functionality, import and export capabilities, real-time monitoring, and a clean, modern user interface. These features provide a solid foundation that you can extend with additional capabilities like cookie categories, preset templates, batch operations, and integration with other developer tools.

Remember to keep your extension updated, respond to user feedback, and continue improving based on real-world usage. With dedication and attention to quality, your cookie editor can become a valuable resource for developers and users alike.

No previous article
No next article