Skip to main content

Documentation Index

Fetch the complete documentation index at: https://extension.js.org/llms.txt

Use this file to discover all available pages before exploring further.

Brave
Chrome
Extension.js
Edge
Vivaldi
Opera
Chromium
Firefox
Waterfox

The cross-browser extension framework

Build browser extensions across all major browsers with one modern workflow. Extension.js handles manifest compilation, browser-specific output, and packaging.

GitHub starsnpm weekly downloadslatest version on npmMIT license
npx extension@latest create my-extension

One manifest.json.
All browsers.

Browser-prefixed keys (chrome:, firefox:, edge:) filter at compile time. Unprefixed keys apply everywhere.

“manifest_version”:3,“name”:“My Extension”,“version”:“1.0.0”,“action”: “default_popup”:“popup.html”,“chrome:background”: “service_worker”:“sw.ts”,“firefox:background”: “scripts”:[“sw.ts”]
Extension.js

Why use a browser extension framework?

Browser extensions need different files, browser APIs, manifest formats, permissions, and build outputs than regular web apps. A browser extension framework removes that glue: one workflow handles Chrome, Edge, and Firefox extension development, manifest filtering per target, reload behavior tuned for service workers and content scripts, and packaging ready for the Chrome Web Store, addons.mozilla.org, and the Edge Add-ons store.

Extension.js gives you that workflow. One JavaScript or TypeScript project, onemanifest.json, separatedist/<browser>outputs, and a CLI that gets out of your way, making it effortless to ship a single codebase as a cross-browser extension.

Watch the full developer workflow

From scaffold to running extension, in one modern toolchain.

Core features for the full extension lifecycle

Build, test, and ship for every browser with one modern extension workflow.

Choose your framework. Keep your workflow.

Start with production-ready templates for React, Preact, Vue, Svelte, TypeScript, or JavaScript.

Coming from another framework?

Most React, Vue, and Svelte source files copy across without rewriting. The work is replacing the bundler config and moving generated manifests back to a real manifest.json.

Backing the open-source core

Sponsors help the project ship faster releases, better developer experience (DX), and long-term reliability for extension teams.

Become a sponsor ⏔

Frequently asked questions

A browser extension is software that adds features to Chrome, Firefox, Edge, or another browser. Extensions inject UI, react to browser events, and call privileged APIs the page itself cannot. See What is a browser extension? for the full primer.
Yes. Extension.js works as a Chrome extension framework, and also as a Firefox and Edge extension framework, from the same project. Pick a target with --browser=chrome, --browser=firefox, or --browser=edge. See Browsers available.
Run npx extension dev and the CLI watches your project for changes. Edits to popups, content scripts, service workers, and options pages trigger the fastest safe update path. The CLI uses hot module replacement (HMR) when supported and targeted reloads otherwise. It automatically resolves common pain points like stale service workers and missed content-script updates.
Extension.js compiles a single codebase into browser-specific output. One set of commands produces correctly packaged builds for Chrome, Edge, Firefox, and other Chromium-based browsers. Each build gets the right manifest format, paths, and platform adjustments. Your continuous integration (CI) pipeline uses the same build step with different browser flags. This reduces drift between store submissions.
Yes. Extension.js ships production-ready templates for React, Preact, Vue, Svelte, TypeScript, and plain JavaScript. You write components the same way you would in any frontend project. Framework choice does not affect the core workflow.
Yes. The toolchain is built around Manifest V3 and the constraints browser stores enforce today: service workers, declarative APIs, updated permissions, and stricter packaging. Manifest V3 (MV3) is the default, not an afterthought. For common pitfalls, see Manifest V3 troubleshooting.
Most Manifest V3 troubleshooting falls into a few buckets: service worker registration and type: "module", the new web_accessible_resources shape, the split between permissions and host_permissions, and declarative_net_request differences in Firefox. See Manifest V3 troubleshooting for the fixes.
Extension.js replaces the extension-specific glue in hand-rolled bundler configs. It handles manifest versioning, multi-browser output, content-script injection, and reload orchestration. Move your source files and manifest into an Extension.js project and the CLI handles the rest. Your React/Vue/Svelte/TS code works without rewriting.
Extension.js supports pnpm, npm, and Yarn workspaces. Path resolution and shared dependencies work across workspace boundaries. Extension.js injects environment variables per browser and build mode, replacing values across JS, JSON, and HTML. Development secrets never leak into production bundles.
Yes. Extension.js is open source under the MIT license. There is no paid tier, usage cap, or telemetry-required mode. The framework is funded by sponsors and contributors. Review the telemetry contract for the exact data shape.
Not yet. --browser=safari is not a CLI target today. You can still author Safari extensions with shared web code and add Safari-specific build steps outside this workflow. See Browsers available for the current scope.
Production builds (extension build) emit only the code your extension references plus a small reload runtime that is removed in production. Dev builds include extra HMR and reload glue. Output sits under dist/<browser> so you can compare sizes between browsers. Content-script bundles stay close to what your source plus your chosen UI framework would produce.
Most React, Vue, and Svelte source files copy across without rewriting. The work is in three places: replacing the bundler config, moving generated manifests back to a hand-authored manifest.json with browser-prefixed keys, and updating package.json scripts. See Migrate from CRXJS for the step-by-step.