| name | extension-migration |
| description | Migrate Chrome extensions from MV2 to MV3. Service workers, declarativeNetRequest, API replacements. Use when: migrate, mv2, mv3, manifest v2. |
Extension Migration (MV2 → MV3)
Official migration guide: https://developer.chrome.com/docs/extensions/develop/migrate
Consider framework adoption: When migrating MV2→MV3, consider adopting WXT or Plasmo for built-in MV3 support, auto-manifest generation, and modern tooling.
Workflow Overview
- Audit existing MV2 extension (APIs, permissions, background scripts)
- Update
manifest_version to 3
- Convert background page → service worker
- Replace deprecated APIs (see Quick Reference below)
- Update permissions, CSP, web_accessible_resources format
- Migrate webRequest → declarativeNetRequest (if blocking)
- Bundle any remote code locally
- Test all functionality across Chrome versions
- Submit to Chrome Web Store
Key Breaking Changes
| Area | MV2 | MV3 |
|---|
| Background | background.scripts/page | background.service_worker |
| Browser action | browser_action / page_action | action |
| Network blocking | webRequest (blocking) | declarativeNetRequest |
| Script injection | tabs.executeScript(string) | scripting.executeScript({func/files}) |
| Remote code | CDN scripts allowed | Must bundle locally |
| CSP | String value | Object {extension_pages: "..."} |
| Web accessible | string[] | object[] with matches field |
| Host permissions | In permissions array | Separate host_permissions array |
| URL helper | chrome.extension.getURL | chrome.runtime.getURL |
| Persistent storage | localStorage | chrome.storage.local |
Migration Priority Checklist
Quick Reference: MV2 → MV3 API Map
chrome.extension.getURL() → chrome.runtime.getURL()
chrome.extension.getBackgroundPage() → chrome.runtime.getBackgroundPage() [deprecated, use messaging]
chrome.tabs.executeScript() → chrome.scripting.executeScript()
chrome.tabs.insertCSS() → chrome.scripting.insertCSS()
chrome.browserAction.* → chrome.action.*
chrome.pageAction.* → chrome.action.*
webRequest (blocking) → declarativeNetRequest
localStorage → chrome.storage.local
XMLHttpRequest → fetch()
setInterval (background) → chrome.alarms
document/window (background) → NOT available in service worker
Manifest Diff
{
"manifest_version": 2,
"background": { "scripts": ["bg.js"], "persistent": false },
"browser_action": { "default_icon": "icon.png" },
"permissions": ["webRequest", "webRequestBlocking", "https://example.com/*"],
"content_security_policy": "script-src 'self'; object-src 'self'",
"web_accessible_resources": ["images/*.png"]
}
{
"manifest_version": 3,
"background": { "service_worker": "bg.js" },
"action": { "default_icon": "icon.png" },
"permissions": ["declarativeNetRequest"],
"host_permissions": ["https://example.com/*"],
"content_security_policy": { "extension_pages": "script-src 'self'; object-src 'self'" },
"web_accessible_resources": [{ "resources": ["images/*.png"], "matches": ["<all_urls>"] }]
}
Reference Files
Related Skills
extension-manifest - MV3 manifest generation
extension-testing - Testing after migration
extension-security - Security audit post-migration