Manifest V3 Migration: What Chrome Extension Developers Need to Know
Chrome's extension platform is undergoing its most significant change in years. The shift from Manifest V2 to Manifest V3 introduces a new set of rules that affect how extensions interact with the browser. The rollout timeline has been in motion since 2018, with Manifest V3 officially beginning to roll out in January 2023. By June 2023, extensions running Manifest V2 will no longer be available on the Chrome Web Store, and non-compliant extensions will eventually be removed entirely.
The core goal of Manifest V3 is improved user safety and a better browser experience. Previously, extensions could rely on code hosted remotely in the cloud, making it difficult to assess risk. Manifest V3 requires extensions to bundle all code they execute, allowing Google to scan for potential vulnerabilities. It also forces extensions to explicitly request permissions for browser modifications.
| January 2023 | June 2023 | January 2024 |
|---|---|---|
| Support for Manifest V2 extensions will be turned off in Chrome’s Canary, Dev, and Beta channels. | The Chrome Web Store will no longer allow Manifest V2 extensions to be published with visibility set to Public. | The Chrome Web Store will remove all remaining Manifest V2 extensions. |
| Manifest V3 will be required for the Featured badge in the Chrome Web Store. | Existing Manifest V2 extensions that are published and publically visible will become unlisted. | Support for Manifest 2 will end for all of Chrome’s channels, including the Stable channel, unless the Enterprise channel is extended. |
Core Differences Between V2 and V3
Several fundamental changes distinguish Manifest V3 from its predecessor. According to Chrome's migration guide, the key differences are:
- Service workers replace background pages.
- The
declarativeNetRequestAPI handles network request modification. - Extensions can only execute JavaScript bundled within their package—no remotely-hosted code.
- Many methods now support
promises, though callbacks remain supported. - Host permissions are specified separately in the
"host_permissions"field. - The content security policy (CSP) is now an object with members for alternative CSP contexts, rather than a string.
For a simple extension that alters a webpage's background, the manifest structure might look like this:
// Manifest V2
{
"manifest_version": 2,
"name": "Shane's Extension",
"version": "1.0",
"description": "A simple extension that changes the background of a webpage to Shane's face.",
"background": {
"scripts": ["background.js"],
"persistent": true
},
"browser_action": {
"default_popup": "popup.html"
},
"permissions": [ "activeTab", ],
"optional_permissions": ["<all_urls>"]
}
// Manifest V3
{
"manifest_version": 3,
"name": "Shane's Extension",
"version": "1.0",
"description": "A simple extension that changes the background of a webpage to Shane's face.",
"background": {
"service_worker": "background.js"
},
"action": {
"default_popup": "popup.html"
},
"permissions": [ "activeTab", ],
"host_permissions": [ "<all_urls>" ]
}
Four Key Areas for Migration
The transition can be broken down into four main areas. Addressing these will get an extension most of the way toward Manifest V3 compliance.
1. Update the Manifest's Basic Structure
The first change is setting "manifest_version" to 3. Beyond that, how you declare background scripts, actions, and APIs changes significantly.
Manifest V3 replaces background pages with a single extension service worker. Register it under "background" using the "service_worker" key, specifying one JavaScript file. While multiple background scripts aren't supported, you can optionally declare the service worker as an ES Module with "type": "module" to enable importing additional code.
The "browser_action" and "page_action" properties are unified into a single "action" property. Similarly, the chrome.browserAction and chrome.pageAction APIs are combined into the Action API, which requires corresponding code changes.
// Manifest V2
"background": {
"scripts": ["background.js"],
"persistent": false
},
"browser_action": {
"default_popup": "popup.html"
},
// Manifest V3
"background": {
"service_worker": "background.js"
},
"action": {
"default_popup": "popup.html"
}
2. Modify Host Permissions
In Manifest V2, host permissions were declared in the "permissions" field. In V3, they're a separate element, specified under "host_permissions". This separation makes permission scopes clearer and helps users understand what an extension can access.
// Manifest V2
"permissions": [
"activeTab",
"storage",
"http://www.css-tricks.com/",
":///*"
]
// Manifest V3
"permissions": [
"activeTab",
"scripting",
"storage"
],
"host_permissions": [
"http://www.css-tricks.com/"
],
"optional_host_permissions": [
":///*"
]
3. Update the Content Security Policy
The CSP declaration changes from a simple string in Manifest V2 to an object with members for different contexts in V3. You'll now specify separate fields for "content_security_policy.extension_pages" and "content_security_policy.sandbox", depending on the types of pages in your extension.
Any references to external domains in the "script-src", "worker-src", "object-src", and "style-src" directives should be removed if present. These updates are necessary to maintain proper security and stability under Manifest V3.
// Manifest V2
"content_security_policy": "script-src 'self' https://css-tricks.com; object-src 'self'"
// Manfiest V3
"content_security_policy.extension_pages": "script-src 'self' https://example.com; object-src
'self'",
"content_security_policy.sandbox": "script-src 'self' https://css-tricks.com; object-src 'self'"
4. Modify Network Request Handling
The chrome.webRequest API, used in Manifest V2 for network request modification, is replaced by declarativeNetRequest in V3. To use the new API, add the declarativeNetRequest permission to your manifest and update your code accordingly.
A key behavioral difference: declarativeNetRequest requires specifying a list of predetermined addresses to block, rather than being able to block entire categories of HTTP requests as chrome.webRequest allowed.
// Manifest V2
"permissions": [
"webRequest",
"webRequestBlocking"
]
// Manifest V3
"permissions": [
"declarativeNetRequest"
]
You'll also need to rewrite your extension's logic to use the declarativeNetRequest API calls instead of chrome.webRequest.
Other Migration Concerns
Beyond the four fundamental areas, several additional considerations may apply depending on your extension's functionality:
- Background script context: With service workers replacing background pages, scripts may need to adapt to the new execution context. This includes handling service worker lifecycle events.
- API unification: The
chrome.browserActionandchrome.pageActionAPIs are now a single API, requiring migration to the new Action API. - Background context methods: Functions like
chrome.runtime.getBackgroundPage(),chrome.extension.getBackgroundPage(),chrome.extension.getExtensionTabs(), andchrome.extension.getViews()aren't compatible with service workers. These may need to be migrated to a message-passing design between contexts and the background service worker. - CORS requests: Content scripts making CORS requests may need to move those requests to the background service worker.
- External code execution: Running external logic via
chrome.scripting.executeScript({code: '...'}),eval(), ornew Function()is no longer allowed. All external code—JavaScript, WebAssembly, and CSS—must be bundled within the extension. Usechrome.runtime.getURL()to build resource URLs at runtime. - Scripting and CSS methods: Several methods that were in the Tabs API have moved to the Scripting API in Manifest V3, so any calls need to be updated to use the correct API.
The full list of changes is extensive, so reviewing Chrome's official migration documentation is worthwhile. But these four areas cover the fundamentals needed to keep an extension working through the transition. Given the strict rollout deadline, developers with existing Manifest V2 extensions should prioritize this migration work now.



