Manifest V2 to V3 Migration Checklist: Complete Chrome Extension Update Guide

13 min read

Manifest V2 to V3 Migration Checklist: Complete Chrome Extension Update Guide

Manifest V2 to V3 Migration Checklist: Complete Chrome Extension Update Guide

Google’s transition from Manifest V2 to Manifest V3 represents the most significant change to the Chrome extension platform since extensions were introduced. With Manifest V2 officially deprecated and existing extensions no longer receiving updates, developers must migrate their extensions to Manifest V3 to maintain functionality and ensure continued support. This comprehensive checklist guides you through every step of the migration process, from understanding the key differences to implementing the new architecture patterns that will keep your extension working in 2025 and beyond.


Understanding Manifest V3 Changes

Before diving into the migration process, it’s essential to understand why Google made these changes and what they mean for your extension. Manifest V3 was designed with three primary goals: improving security, enhancing performance, and providing better user privacy controls.

Key Architectural Differences

The most fundamental change in Manifest V3 is the replacement of persistent background pages with service workers. In Manifest V2, background scripts ran as persistent pages that stayed loaded throughout the browser session, allowing them to maintain state and respond to events instantly. This model, while convenient, had significant drawbacks. Persistent background pages consumed memory continuously, even when the extension wasn’t actively doing anything. They also presented security vulnerabilities because they had broad access to the extension’s entire API surface.

Manifest V3 addresses these issues by introducing service workers that spin up when needed and terminate when idle. This approach dramatically reduces memory consumption and improves the overall performance of the browser. However, it requires developers to rethink how they handle asynchronous operations and state management, as service workers don’t maintain memory between invocations.

Another major change involves network request modification. In Manifest V2, developers could use the webRequest API to block or modify network requests synchronously. Manifest V3 replaces this with the declarativeNetRequest API, which handles request modification declaratively through rulesets. This shift gives users more control over what extensions can do while improving performance by moving request processing out of the critical path.

Deprecated Features You Need to Address

Several features available in Manifest V2 are no longer available or have been significantly modified in Manifest V3. The chrome.experimental.* APIs have been removed entirely, so any experimental features your extension relied on need to be replaced with stable alternatives or removed. The background permission in its V2 form is gone, replaced by the service_worker specification. Remote code execution through eval() and similar mechanisms is now prohibited, meaning all your extension’s code must be bundled within the package.

The chrome.proxy API has been restricted to force-installed extensions only, making it unavailable for most extensions in the Chrome Web Store. Similarly, the chrome.debugger API cannot be used in extensions distributed through the store. These restrictions reflect Google’s focus on user privacy and security, but they require developers to find alternative approaches for functionality that previously relied on these APIs.


Pre-Migration Preparation

Successful migration begins with thorough preparation. Before making any code changes, you need to audit your extension’s current functionality and dependencies.

Step 1: Audit Your Current Extension

Start by examining your current manifest.json file to understand what permissions, APIs, and resources your extension currently uses. List every API your background scripts and content scripts call, and identify which ones have changed in Manifest V3. Pay particular attention to any use of deprecated features like webRequest blocking, eval(), or experimental APIs.

Review your extension’s architecture and identify any state that’s maintained in background page variables. This state will need to be migrated to either Chrome’s storage API or a combination of storage and runtime state management. Document all event listeners your background page registers, as these will need to be re-registered each time your service worker wakes up.

Step 2: Update Your Development Environment

Ensure you’re using a recent version of Chrome that fully supports Manifest V3 features. At minimum, you should be developing with Chrome 88 or later, though newer versions provide better support for all V3 features. Update your local development tools and consider using the Chrome Extension Manifest V3 Migration Tool if Google has released one for your specific use case.

Create a backup of your current extension codebase before beginning the migration. While version control should handle this, having a separate backup ensures you can easily roll back if issues arise during the migration process.

Step 3: Review Chrome Web Store Compliance

Familiarize yourself with the current Chrome Web Store policies, as they’ve been updated to align with Manifest V3 requirements. Extensions that don’t comply with these policies won’t be accepted for publication. Pay special attention to policies around data handling, user consent, and functionality that might be restricted in the Manifest V3 environment.


Manifest File Migration

The manifest.json file is the foundation of your extension, and updating it correctly is the first technical step in your migration.

Step 4: Update Manifest Version

Change the manifest_version field from 2 to 3. This single change triggers the Manifest V3 behavior, but many other manifest changes are required to make your extension functional.

{
  "manifest_version": 3,
  "name": "Your Extension Name",
  "version": "2.0",
  ...
}

Step 5: Migrate Background Configuration

Replace the background key with the service worker configuration. Instead of specifying scripts and pages, you now point to a single service worker file.

{
  "background": {
    "service_worker": "background.js"
  }
}

Remove any persistent_background setting, as service workers are inherently non-persistent. If you were using a background page HTML file, you’ll need to convert its functionality to JavaScript that runs in the service worker context.

Step 6: Review and Update Permissions

Manifest V3 has a more granular permission system. Review every permission your extension requests and ensure each is necessary. Some permissions that were optional in V2 are now required for specific functionality, while others have been restricted or moved to optional categories.

For example, if your extension needs to access tabs or bookmarks, those permissions should be specified in the permissions array. If you’re using the declarativeNetRequest API, add the appropriate permission and define your ruleset in the manifest.


Background Script Migration

The background script migration is typically the most complex part of moving to Manifest V3, requiring significant architectural changes.

Step 7: Convert to Service Worker Architecture

Your background script becomes a service worker in Manifest V3. This means it no longer maintains state between invocations. Every time your service worker wakes up to handle an event, it starts fresh with no memory of previous executions.

To handle this, migrate all persistent state to Chrome’s storage API. Use chrome.storage.local for extension-specific data or chrome.storage.sync for user data that should follow their account. Any initialization logic that previously ran once when the background page loaded now needs to run every time the service worker starts.

// Before (Manifest V2)
let cachedData = null;

chrome.runtime.onStartup.addListener(() => {
  loadInitialData();
});

function loadInitialData() {
  // Load and cache data
  cachedData = await fetchData();
}

// After (Manifest V3)
chrome.runtime.onStartup.addListener(async () => {
  // Load data from storage each time
  const data = await chrome.storage.local.get('cachedData');
});

chrome.runtime.onInstalled.addListener(async () => {
  // Initialize on installation
});

Step 8: Migrate Event Listeners

In Manifest V2, event listeners could be registered once and would remain active. In Manifest V3, you must re-register event listeners each time your service worker starts. Chrome provides the addListener method just as before, but you need to ensure listeners are properly registered in the service worker’s top-level scope.

// Service worker (Manifest V3)
chrome.runtime.onMessage.addListener(handleMessage);
chrome.tabs.onUpdated.addListener(handleTabUpdate);
chrome.storage.onChanged.addListener(handleStorageChange);

// These listeners will be re-registered each time the service worker starts

Step 9: Handle Asynchronous Operations Carefully

Service workers can be terminated at any time when idle. Any asynchronous operation that takes too long might be interrupted. For operations that need to complete regardless of service worker state, use the chrome.storage API to persist progress, or implement retry logic that can resume from a saved state.

When responding to events that require async operations, ensure your listeners return true to indicate you’ll send a response asynchronously, and complete all async work before the service worker terminates.


Content Script Updates

Content scripts require fewer changes than background scripts, but some adjustments are necessary.

Step 10: Update Content Script Declaration

If you specify content scripts in your manifest, they work similarly in Manifest V3. However, pay attention to the run_at setting to ensure your scripts inject at the appropriate time.

{
  "content_scripts": [
    {
      "matches": ["<all_urls>"],
      "js": ["content.js"],
      "run_at": "document_idle"
    }
  ]
}

Step 11: Update Communication Patterns

If your content scripts communicate with background scripts using message passing, ensure your message handlers are properly implemented in the service worker. The message passing API itself hasn’t changed, but the context in which handlers run has.

// Content script
chrome.runtime.sendMessage({ type: 'GET_DATA' }, response => {
  // Handle response
});

// Service worker
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
  if (message.type === 'GET_DATA') {
    // Process request
    sendResponse({ data: 'some data' });
  }
});

Network Request Modification

If your extension modifies or blocks network requests, this is one of the most significant changes in Manifest V3.

Step 12: Migrate from webRequest to declarativeNetRequest

The webRequest API in Manifest V2 allowed you to intercept and modify requests synchronously. In Manifest V3, you use the declarativeNetRequest API with rules that define how requests should be handled.

First, add the required permissions:

{
  "permissions": ["declarativeNetRequest"],
  "host_permissions": ["<all_urls>"]
}

Then create a rules file:

[
  {
    "id": 1,
    "priority": 1,
    "action": {
      "type": "block"
    },
    "condition": {
      "urlFilter": "example.com/tracker",
      "resourceTypes": ["script"]
    }
  }
]

Load these rules in your service worker:

chrome.declarativeNetRequest.updateSessionRules({
  addRules: [
    {
      id: 1,
      priority: 1,
      action: { type: 'block' },
      condition: {
        urlFilter: 'example.com/tracker',
        resourceTypes: ['script']
      }
    }
  ]
});

Testing Your Migration

Thorough testing is crucial to ensure your migrated extension works correctly.

Step 13: Test in Unpacked Mode

Load your extension as an unpacked extension in Chrome to test functionality. Enable developer mode in chrome://extensions/, click “Load unpacked,” and select your extension directory. Test every feature thoroughly, paying special attention to:

  • Background/service worker functionality that may behave differently
  • State persistence across service worker restarts
  • Message passing between components
  • Network modification rules

Step 14: Test Edge Cases

Manifest V3 service workers behave differently in edge cases. Test scenarios like:

  • What happens when Chrome restarts and the service worker hasn’t run recently
  • How your extension handles multiple rapid events
  • Memory usage over extended periods with the new architecture
  • Behavior when network conditions change

Step 15: Verify Extension Performance

Use Chrome’s developer tools to profile your extension’s performance. Pay attention to:

  • Memory consumption compared to the Manifest V2 version
  • Service worker startup time
  • Event handling latency

Deployment Considerations

Once testing is complete, you need to carefully plan your deployment.

Step 16: Update Version Number

Increment your extension’s version number appropriately. Since this is a migration with potential breaking changes, consider whether this warrants a major version bump depending on your version scheme.

Step 17: Prepare Release Notes

Document all changes in your extension’s listing, especially any functionality that might have changed due to the migration. Users should understand what to expect from the update.

Step 18: Consider a Staged Rollout

If your extension has a large user base, consider using the Chrome Web Store’s staged rollout feature to gradually release the update. This allows you to catch any issues with a small percentage of users before a full release.


Troubleshooting Common Issues

Even with careful planning, you may encounter issues during migration.

Service Worker Not Starting

If your service worker isn’t triggering, check that it’s properly registered in the manifest and that there are no syntax errors in the file. Use the Service Worker section in Chrome’s developer tools to debug.

State Not Persisting

If your extension’s state seems to be lost, ensure you’re properly saving to and loading from Chrome’s storage API. Remember that service workers don’t maintain memory between invocations.

API Not Available

Some APIs that worked in Manifest V2 require different permissions or have different interfaces in Manifest V3. Double-check the API documentation for any changes.


Conclusion

Migrating from Manifest V2 to Manifest V3 is a significant undertaking, but it’s essential for the continued functionality of your Chrome extension. While the changes require careful attention to architecture and async patterns, the result is a more secure, performant extension that better serves your users and complies with modern web standards.

By following this checklist systematically, you can ensure a smooth transition. Remember to test thoroughly, document your changes, and monitor user feedback after deployment. The Manifest V3 platform provides excellent capabilities once you understand its patterns, and your migrated extension will be well-positioned for the future of Chrome extension development.

The key to successful migration is patience and thorough testing. Take the time to understand the fundamental architectural changes, implement them correctly, and validate every feature works as expected. Your users will appreciate the improved performance and reliability of your Manifest V3 extension.


Additional Resources

No previous article
No next article