Open to SWE / AI Engineering roles β€” Let's talk
Skip to content

How Chrome Extensions Actually Work in 2026 (Manifest V3 Explained)

Software engineeringMay 3, 2026

Get the mental model right before writing a single line of extension code.

How Chrome Extensions Actually Work in 2026 (Manifest V3 Explained)

Before you write a single line of extension code, you need the mental model. Most beginners jump into tutorials, copy-paste a manifest.json, and then get stuck the moment they need two parts of their extension to talk to each other.

This post is the architecture overview. No code β€” just the structure that makes everything else click.

MV2 Is Dead. MV3 Is the Only Path.

Manifest V2 extensions can no longer be uploaded to the Chrome Web Store. Existing MV2 extensions are being gradually disabled. If you're starting today, MV3 isn't optional β€” it's the only option.

The biggest change: persistent background pages are gone. MV3 replaces them with service workers that start on demand and terminate when idle. This has real consequences for how you manage state (more on that in How to Persist State in a Chrome Extension).

Other MV3 changes worth knowing:

  • chrome.scripting replaces inline chrome.tabs.executeScript
  • fetch replaces XMLHttpRequest in background contexts
  • Content Security Policy is stricter β€” no remote code execution. Any extension that loaded scripts from a remote URL (a common pattern for feature flags or A/B testing) must now bundle those scripts locally.
  • Declarative Net Request replaces the blocking webRequest API. This is what broke many legacy ad blockers β€” DNR requires you to declare filtering rules upfront rather than intercepting and inspecting each request at runtime.

The Four Components

Every Chrome extension is built from up to four pieces. Not all extensions use all four, but understanding each one is essential.

1. Popup

The small UI that appears when a user clicks your extension icon in the toolbar.

  • It's just an HTML page β€” you can use vanilla JS, React, Vue, whatever fits
  • It has no persistent state β€” every time the popup closes, it's destroyed and re-created on next open
  • It can call chrome.* APIs directly
  • It communicates with other components via message passing

Think of it as a tiny single-page app that lives in a bubble. If you need to do something after the popup closes, that work belongs in the service worker.

Ready to build one? Build Your First Chrome Extension with TypeScript from Scratch walks through a complete popup setup with Vite and CRXJS.

2. Background Service Worker

The "brain" of your extension. It runs in the background, listens for events, and coordinates everything.

  • Runs as a service worker, not a regular page β€” no DOM access, no window, no document
  • Terminates when idle (usually after ~30 seconds of inactivity per Chrome's extension service worker docs) and restarts when an event fires
  • Handles events: alarms firing, messages from popup or content scripts, tab updates, chrome.runtime.onInstalled, web navigation β€” any registered listener wakes it up
  • This is where you put logic that needs to survive after the popup closes

The termination behavior is the single most important thing to understand in MV3. If you store data in a global variable like let userSettings = {}, it will vanish after ~30 seconds of idle β€” silently, with no error. Use chrome.storage instead β€” see How to Persist State in a Chrome Extension.

3. Content Script

Code that runs inside web pages the user visits.

  • Has full DOM access β€” it can read, modify, and inject elements into the page
  • Runs in an isolated world β€” it shares the DOM but not JavaScript variables with the page. If a webpage declares let userID = 123, your content script cannot read that variable β€” but it can read <div id="user">123</div>.
  • Cannot call most chrome.* APIs directly β€” it must send messages to the service worker
  • Injected via the content_scripts field in manifest.json or dynamically via chrome.scripting.executeScript

Content scripts are how extensions interact with websites. An ad blocker's content script removes ad elements. A password manager's content script fills in form fields.

⚠️ Common misconception: Content scripts cannot call chrome.storage, chrome.tabs, or most other Chrome APIs directly. Attempting it silently fails. If you need extension APIs from a content script, send a message to the service worker and let it handle the API call.

4. Options Page

A full-page settings UI for your extension.

  • Accessible via right-clicking the extension icon β†’ "Options", or directly from chrome://extensions β†’ "Details" β†’ "Extension options"
  • Just an HTML page like the popup, but it opens in a full tab
  • Good for configuration that doesn't fit in a small popup
  • Often used as an onboarding flow β€” link to the options page from chrome.runtime.onInstalled to greet new users

Most simple extensions skip this. Once your extension has enough settings, add one.

How the Components Connect

Here's the part most tutorials skip. These four components run in separate execution contexts. They can't call each other's functions or share variables directly. They communicate through message passing.

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”     messages     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚   Popup     β”‚ ◄──────────────► β”‚  Service Worker   β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                        β–²
                                        β”‚ messages
                                        β–Ό
                                 β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                                 β”‚  Content Script   β”‚
                                 β”‚  (inside webpage) β”‚
                                 β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

The service worker is the hub. The popup talks to it. Content scripts talk to it. The popup and content scripts don't typically talk to each other directly β€” they go through the service worker.

This message-passing architecture is where most beginners get stuck. If you want to get it right with TypeScript, Chrome Extension Message Passing with TypeScript: The Complete Guide covers typed messages, discriminated unions, and the async gotchas.

The Manifest Ties It All Together

The manifest.json is the configuration file that declares which components your extension uses:

manifest.json
β”œβ”€β”€ action.default_popup        β†’ "popup.html"
β”œβ”€β”€ background.service_worker   β†’ "service-worker.js"
β”œβ”€β”€ content_scripts             β†’ [{ matches: ["*://*.example.com/*"], js: ["content.js"] }]
β”œβ”€β”€ options_page                β†’ "options.html"
└── permissions                 β†’ ["storage", "tabs", "activeTab"]

If a component isn't declared in the manifest, it doesn't exist. The manifest is also where you declare permissions β€” and getting permissions right is the difference between smooth Web Store approval and repeated rejections.

Which Components Do You Actually Need?

It depends on what your extension does:

Popup-only (simplest) β€” A calculator, color picker, or quick-reference tool. Just a popup, no background or content scripts.

Popup + Service Worker β€” The popup triggers actions, the service worker handles them. Good for extensions that need to do work after the popup closes, like setting alarms or making API calls.

Popup + Service Worker + Content Script β€” The most common architecture for extensions that interact with web pages. This is what ResistGate uses: the content script intercepts navigation, the service worker manages challenge state, and the popup shows settings.

Content Script only β€” Auto-running modifications to web pages. Ad blockers, readability tools, dark mode injectors.

Quick decision tree:

Does your extension modify web pages?
  Yes β†’ you need a Content Script
  No  β†’ popup-only may be enough

Does work need to continue after the popup closes?
  Yes β†’ you need a Service Worker

Does your extension have enough settings to warrant a full page?
  Yes β†’ add an Options Page

Mental Model Summary

Think of a Chrome extension as a set of isolated programs that share a communication bus:

  • The popup is the face β€” small, temporary, user-triggered
  • The service worker is the brain β€” event-driven, stateless (by design), always listening
  • The content script is the hands β€” it touches web pages
  • The options page is the settings panel
  • Messages are how they all talk
  • The manifest is the wiring diagram

Get this model in your head, and every tutorial, API doc, and Stack Overflow answer will make more sense.

Where to Go from Here

Now that you have the architecture, go build something:

  1. Hands-on start β€” Build Your First Chrome Extension with TypeScript from Scratch takes you from empty folder to working popup in 30 minutes.
  2. Communication β€” Chrome Extension Message Passing with TypeScript is the guide you'll need the moment your extension has more than one component.
  3. State management β€” How to Persist State in a Chrome Extension covers the storage patterns that MV3's service worker model demands.

Beyond the Basics

Once you have the four-component model down, these patterns are worth knowing exist (even if you don't need them yet):

  • Side panels (chrome.sidePanel) β€” persistent UI that lives in Chrome's sidebar, stays open across page navigations
  • Offscreen documents β€” a hidden page that lets service workers use DOM APIs they otherwise can't access
  • Dynamic content script injection β€” inject scripts on demand via chrome.scripting.executeScript instead of declaring them statically in the manifest
  • Keep-alive patterns β€” techniques for keeping the service worker alive during long-running operations

These are advanced. Get the fundamentals solid first.


This is part of the Chrome Extensions from Zero to Product series, where I walk through everything I learned building ResistGate and Amethyst β€” from first popup to published product.

Building in public

Follow the journey

as I build AI tools, products, and a serious founder life.

No spam. Unsubscribe anytime.

How Chrome Extensions Actually Work in 2026 (Manifest V3 Explained) | Orlando Ascanio