Manifest V3 in practice: what actually changed for Chrome extensions
Manifest V3 has been the default since Chrome phased out V2, and a lot of 2021-era tutorial code silently breaks against it. Having shipped a dozen client extensions on MV3 — scrapers, outreach automators, data capture tools — here's the honest cheat sheet of what changed and how to design for it.
1. Background pages are gone — long live service workers
The persistent background page is replaced by a service worker that can be killed at any moment. Any state you keep in a global variable is a bug waiting to happen.
// ❌ breaks after the worker sleeps let queue = []; chrome.runtime.onMessage.addListener((msg) => queue.push(msg)); // ✅ survives sleep — state lives in storage chrome.runtime.onMessage.addListener((msg) => chrome.storage.session.append(msg) // or your own queue in chrome.storage.local );
Design rule: the service worker is a router, not a brain. Persist state, react to events, get out.
2. Remote code is banned
You can no longer eval, new Function(), or load scripts from your
server into the extension context. Everything ships inside the package. Practical
consequences:
- Bundle your dependencies at build time (Vite/esbuild works great).
- Config-driven behaviour is fine — fetch a JSON file and branch on it, just don't fetch and execute code.
- Heavy templates? Ship them as assets and render them into the page, not the other way around.
3. <script> injection into pages is restricted
MV3 content scripts live in an isolated world. To run code in the page's own context
(reading page-set variables, hooking fetch), inject a script file from your bundle via
the world: 'MAIN' option — inline strings won't fly:
chrome.scripting.executeScript({
target: { tabId },
world: 'MAIN',
files: ['page-hook.js']
});
4. Permissions got scarier — and more honest
Chrome reviews extensions with broad <all_urls> host permissions hard. The
pattern I use for client tools:
- Use
activeTab+scriptingwherever the action is user-initiated — this alone passes review for most internal tools. - Request broad hosts only when the extension truly needs background monitoring, and expect a review period measured in days, not hours.
- Private distribution via the Chrome Web Store (unlisted) keeps a team-only tool out of public search while staying auto-updated.
What this means for business extensions
The teams I build for care about one thing: does it work every morning. MV3's sleeping
worker means you design like a backend engineer — events in, storage as source of truth,
alarms (chrome.alarms) for anything periodic. Do that and MV3 is not a limitation;
it's free reliability.
Hiring? I build Manifest V3 extensions end-to-end — from scoping to private Web Store publication.