Back to posts

The Modern Browser Extension Playbook: Build, Ship, and Scale Extensions the Right Way

12/12/2025

Building a production-grade browser extension today is very different from the early days of Manifest V2. Modern extensions require proper architecture, tooling, automation, and a clear understanding of how different contexts communicate.
This playbook walks through the entire lifecycle — from ideation to deployment and long-term maintenance.


Phase 0: Foundation & Ideation

Before writing any code, clarify the purpose and interaction model of your extension.

1. Define the Core Problem

Identify the exact problem you’re solving.
Vague: “Helps users save articles.”
Precise: “Saves the active tab’s article to a backend read-later service with one click.”

You need that level of clarity to avoid feature creep.

2. Choose the Extension Type

Extensions can expose multiple UX surfaces:

  • Popup (Browser Action): The toolbar icon opens a small UI.
  • Page Action: Rare now — appears only on specific pages.
  • Content Script: Injected into websites, modifies or reads page content.
  • Background Script (Service Worker): Manages events, permissions, and state.
  • Options Page: Full settings page.
  • DevTools Panel: Custom tools integrated into browser DevTools.
  • Override Pages: Replace the New Tab page, history page, etc.

3. Map the User Flow

Sketch the entire user journey:
Install → Grant permissions → Use popup → Trigger action → View result.

This defines your UI, architecture, and data flow.

4. Research the Competition

Audit extensions solving similar problems. Identify gaps. Don’t copy; out-execute.


Phase 1: Choose the Right Tech Stack

Plain JavaScript and manual builds slow you down. A modern extension needs a modern stack.

⭐ Recommended Stack

Category Recommendation Rationale
Build Tool Vite Fast HMR, stable output, minimal config.
Framework React / Vue / Svelte Component-based, scalable, familiar.
Vite Plugin @crxjs/vite-plugin Handles Manifest V3, HMR, bundling, and polyfills.
Language TypeScript Mandatory for maintainability.
Browser API Wrapper webextension-polyfill One API that works across Chrome, Firefox, Edge, Safari.
Styling Tailwind CSS Fast UI iteration, removes CSS boilerplate.
Component Library Shadcn/UI or Radix Accessible primitives for popup UIs.

Recommended Project Structure

my-extension/
├── public/
│ ├── icon16.png
│ ├── icon48.png
│ └── icon128.png
├── src/
│ ├── background/ # Service Worker
│ ├── content/ # Content Scripts
│ ├── popup/ # Popup UI
│ ├── options/ # Settings page
│ ├── components/ # Shared UI components
│ ├── lib/ # Storage, API, helpers
│ └── manifest.json
├── package.json
└── vite.config.ts

markdown Copy code

This layout avoids chaos later when you add more surfaces (DevTools, offscreen pages, etc.).


Phase 2: Architecture & Development

1. Manifest V3 Is Mandatory

Key differences from V2:

  • Background pages → Service Workers
  • No remote-code execution
  • Stricter CSP
  • Unified action API
  • Offscreen documents required for DOM access

Ignoring these early guarantees a painful refactor later.

2. Master Extension Communication

Every part of your extension runs in its own isolated world. You must pass messages explicitly.

From To Method Purpose
Popup Background runtime.sendMessage Trigger actions or read state
Background Popup onMessage Send updates back
Content Script Background sendMessage Share page data
Background Content Script tabs.sendMessage Inject commands / data
Page Scripts Content Scripts window.postMessage Bypass sandboxing

If you misunderstand this, your extension will behave unpredictably.

3. Offscreen Documents

Background service workers cannot use DOM APIs, so tasks like:

  • MediaRecorder
  • HTML parsing
  • Canvas operations
  • Audio processing

must run in an offscreen document.

You create one with:

await chrome.offscreen.createDocument({
  url: 'offscreen.html',
  reasons: ['USER_MEDIA'],
  justification: 'Record audio from microphone'
});
This is essential for modern extensions (screen recorders, converters, editors, etc.).

Phase 3: Build & Deployment
1. Local Development with Vite + CRXJS
Steps:

Run npm run dev.

Open chrome://extensions.

Enable Developer Mode.

Load the dist folder as an unpacked extension.

CRXJS handles HMR even for content scripts — something most toolchains break.

2. Publishing to Chrome Web Store
Create a developer account and pay the one-time fee.

Upload a zipped dist folder.

Add screenshots, description, and privacy policy.

Submit for review.

Approval can take hours or days depending on permissions.

3. Publishing to Firefox Add-ons (AMO)
If you use webextension-polyfill, most code works without modification.
Firefox reviews are stricter but usually faster.

4. CI/CD with GitHub Actions
A recommended workflow:

On push → lint, test, build.

On release tag → build, zip, attach to GitHub Release.

Optional → auto-upload to Chrome/Firefox for beta channels.

Automate everything except writing the code.

Phase 4: Testing & Quality Assurance
1. Unit Testing
Use Vitest or Jest for:

API helpers

Storage wrappers

Pure logic functions

2. E2E Testing
Use Playwright or Cypress to:

Launch a browser with the extension loaded

Interact with the popup

Interact with content scripts

Verify background events

Few developers test extensions properly — that’s why so many break.

3. Manual Testing Checklist
Chrome / Firefox / Edge

Windows / macOS / Linux

Install → Upgrade → Reinstall flows

Behavior with other extensions installed

Permission prompts

Offline mode

Phase 5: Post-Launch Maintenance
Launching is the midpoint, not the finish line.

1. Error Tracking
Use Sentry to capture:

Runtime errors

Unhandled promise rejections

Background script failures

You will find issues users never report.

2. Privacy-Focused Analytics
Plausible or Fathom works well.
Avoid Google Analytics — heavy, slow, and poor fit for extensions.

3. Feedback Loop
Provide a link to your GitHub Issues page or a lightweight feedback form.

4. Regular Updates
Browsers ship changes constantly.
Update dependencies frequently to avoid silent failures.

Conclusion
A modern browser extension is essentially a multi-surface web application with strict sandboxing and event-driven architecture.
If you plan correctly, choose the right tools, understand Manifest V3 limitations, and automate your pipeline, you can ship stable, scalable extensions with far less friction.

This playbook gives you the foundation to build extensions that don’t fall apart as the codebase grows — and prepares you for long-term maintenance across every major browser.
#Browser Extensions#Web Development#Manifest V3#Vite#React
0views