Welcome Getting started API Reference Samples
Welcome Getting started API Reference Samples

Migrating to Manifest V3

Getting you headed in the right direction.

Published on

This guide provides developers with the information they need to begin migrating an extension from Manifest V2 to Manifest V3 (MV3). Some extensions will require very little change to make them MV3 compliant, while others will need to be redesigned to some degree. Developers experienced with MV2, and who are creating new MV3 extensions, may also find this helpful. For a quick reference guide see the migration checklist.

Manifest V3 offers a number of improvements reflecting the aims of our platform vision.

Feature summary #

There are a number of new features and functional changes for extensions using MV3:

For a fuller description of these changes, see the MV3 Overview.

Updating the manifest.json file #

To use the features of MV3, you need to first update your manifest file. Naturally, you'll change the manifest version to "3", but there are a number of other things you need to change in the manifest file: host permissions, content security policy, action declarations, and web-accessible resources.

Manifest version #

Changing the value of the manifest_version element is the key to upgrading your extension. This determines whether you're using the MV2 or MV3 feature set:

// Manifest v2

"manifest_version": 2
// Manifest v3

"manifest_version": 3

Host permissions #

In MV3, you'll need to specify host permissions separately from other permissions:

// Manifest v2
"permissions": [
"tabs",
"bookmarks",
"http://www.blogger.com/",
],
"optional_permissions": [
"*://*/*",
"unlimitedStorage"
]
// Manifest v3
"permissions": [
"tabs",
"bookmarks"
],
"optional_permissions": [
"unlimitedStorage"
],
"host_permissions": [
"http://www.blogger.com/",
"*://*/*"
],
Warning

You do not have to declare content script match patterns in host_permissions in order to inject content scripts. However, they are treated as host permissions requests by the Chrome Web Store review process.

Content security policy #

An extension's content security policy (CSP) was specified in MV2 as a string; in MV3 it is an object with members representing alternative CSP contexts:

// Manifest v2

"content_security_policy": "..."
// Manifest v3

"content_security_policy": {
"extension_pages": "...",
"sandbox": "..."
}

extension_pages: This policy covers pages in your extension, including html files and service workers.

These page types are served from the chrome-extension:// protocol. For instance, a page in your extension is chrome-extension://<extension-id>/foo.html.

sandbox: This policy covers any sandboxed extension pages that your extension uses.

In addition, MV3 disallows certain CSP modifications for extension_pages that were permitted in MV2. The script-src, object-src, and worker-src directives may only have the following values:

  • self
  • none
  • Any localhost source, (http://localhost, http://127.0.0.1, or any port on those domains)

CSP modifications for sandbox have no such new restrictions.

Action API unification #

In MV2, there were two different APIs to implement actions: browser_action and page_action. These APIs filled distinct roles when they were introduced, but over time they've become redundant so in MV3 we are unifying them into as single action API:

// Manifest v2

// manifest.json
{
"browser_action": {},
"page_action": {}
}

// background.js
chrome.browserAction.onClicked.addListener(tab => {});
chrome.pageAction.onClicked.addListener(tab => {});
// Manifest v3

// manifest.json
{
"action": {}
}


// background.js
chrome.action.onClicked.addListener(tab => {});

In order to aid with the migration process, the Action API can be used in MV2 beginning with Chrome 88.

Web-accessible resources #

This change limits access to extension resources to specific sites/extensions. Instead of providing a list of files, you now provide a list of objects, each of which can map to a set of resources to a set of URLs and extension IDs:

// Manifest v2

"web_accessible_resources": [
<files>
]
// Manifest v3

"web_accessible_resources": [{
"resources": [<resources>],
"matches": [<urls>],
"extension_ids": [<keys>],
optional "use_dynamic_url": boolean
}]

The matches, extension_ids, and use_dynamic_url keys are not available yet. Support for these properties will be coming in a future release.

Previously, the list of web accessible resources applied to all websites and extensions, which created opportunities for fingerprinting or unintentional resource access. The updated API lets extensions more tightly control what other sites or extensions can access extension resources.

Code execution #

MV3 imposes new restrictions that limit an extension's ability to execute unreviewed JavaScript through a combination of platform changes and policy limitations.

Many extensions are unaffected by this change. However, if your MV2 extension executes remotely hosted scripts, injects code strings into pages, or evals strings at runtime, you'll need to update your code execution strategies when migrating to MV3.

Remotely hosted code #

Remotely hosted code refers to any code that is not included in an extension's package as a loadable resource. For example, both of the following are considered remotely hosted code:

  • JavaScript files pulled from a remote server
  • a code string passed into eval at runtime

In MV3, all of your extension's logic must be bundled with the extension. You can no longer load and execute a remotely hosted file. A number of alternative approaches are available, depending on your use case and the reason for remote hosting. Two such approaches are:

Configuration-driven features and logic — In this approach, your extension loads a remote configuration (for example a JSON file) at runtime and caches the configuration locally. The extension then uses this cached configuration to decide which features to enable.

Externalize logic with a remote service — Consider migrating application logic from the extension to a remote web service that your extension can call. (Essentially a form of message passing.) This provides you the ability to keep code private and change the code on demand while avoiding the extra overhead of resubmitting to the Chrome Web Store.

Executing arbitrary strings #

The code property from executeScript's details object is no longer available in MV3.

Instead of executing a string, you should move your code into a static JavaScript file included in the bundle, then execute it using the executeScript method's file property:

// Manifest v2

// background.js
chrome.tabs.executeScript({
code: 'alert("test!")'
});
// Manifest v3

// background.js
chrome.scripting.executeScript({
file: 'content-script.js'
});

// content-script.js
alert("test!");

Alternatively, if the logic being executed can be neatly wrapped in a function call, you can use the new function property:

// Manifest v2

// background.js
chrome.tabs.executeScript({
code: 'alert("test!")'
});
// Manifest v3

// background.js
function showAlert() {
alert("test!");
}

chrome.scripting.executeScript({
function: showAlert
});

Background service workers #

Background pages in MV2 are replaced by service workers in MV3: this is a foundational change that affects most extensions.

Service workers are event based, and like event pages they do not persist between invocations. This change generally requires some redesign, with a number of factors to consider: see Migrating from Background Pages to Service Workers for additional details.

In order to aid with the migration process, MV2 extensions can use background service workers as of Chrome 87.

Modifying network requests #

There is a new declarativeNetRequest for network request modification, which provides an alternative for much of the webRequest AP's functionality.

When can you use blocking webRequest? #

The blocking version of the webRequest API still exists in MV3 but its use is restricted to force-installed extensions only. See Chrome Enterprise policies: ExtensionSettings, ExtensionInstallForcelist.

All other extensions must now use declarativeNetRequest for network request modification. This moves the actual modification of the network request into the Chrome browser: the extension no longer can read the actual network request, and in most cases needs no host permissions.

Request redirects and header modifications do require the user to grant host permissions.

How do you use declarativeNetRequest? #

Instead of reading the request and programmatically altering it, your extension specifies a number of rules, which map a set of conditions to corresponding actions. See the declarativeNetRequest reference documentation for a more detailed description of rules.

This feature allows content blockers and other request-modifying extensions to implement their use cases without requiring host permissions, and without needing to read the actual requests.

In order to aid with the migration process, the declarativeNetRequest API is available for use in MV2 extensions as of Chrome 84.

Conditional permissions and declarativeNetRequest #

Most use cases for declarativeNetRequest don't require any host permissions at all. However, some do.

Request redirects and header modifications do require the user to grant host permissions.

When extensions require host permissions for these use cases, we recommend a "tiered" permissions strategy. This means implementing the extension's core functionality without using these permissions; putting the more advanced use cases behind an optional permission.

This approach allows privacy-conscious users to withhold those permissions and still use much of the extension's functionality. This means that developers can implement many common use cases, such as content-blocking functionality, without requiring any host permissions.

Sunset for deprecated APIs #

There are a number of APIs that have long been deprecated. Manifest V3 finally removes support for these deprecated APIs. These include:

  • chrome.extension.sendRequest()
  • chrome.extension.onRequest
  • chrome.extension.onRequestExternal
  • chrome.extension.lastError
  • chrome.extension.getURL()
  • chrome.extension.getExtensionTabs()
  • chrome.tabs.Tab.selected
  • chrome.tabs.sendRequest()
  • chrome.tabs.getSelected()
  • chrome.tabs.getAllInWindow()
  • chrome.tabs.onSelectionChanged
  • chrome.tabs.onActiveChanged
  • chrome.tabs.onHighlightChanged

As well as the undocumented:

  • chrome.extension.sendMessage()
  • chrome.extension.connect()
  • chrome.extension.onConnect
  • chrome.extension.onMessage

If your extensions use any of these deprecated APIs, you'll need to make the appropriate changes when you migrate to MV3.

We serve cookies on this site to analyze traffic, remember your preferences, and optimize your experience.