

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.
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”,“chromium:background”: “service_worker”:“sw.ts”,“firefox:background”: “scripts”:[“sw.ts”]
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, one
manifest.json
, separate
dist/<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.
Compile one extension into browser-specific output
Build once for Chrome, Edge, Firefox, and custom Chromium or Gecko targets. Browser-specific output stays predictable across development, testing, and release builds.
Keep extension structure explicit
Manifest, paths, and emitted assets stay in sync as your extension grows.

Tune behavior with config
Override defaults through extension.config.js for
custom builds.
Bring your preferred stack
Start with React, Preact, Vue, Svelte, TypeScript, or JavaScript. Keep one workflow so your team picks the right UI model without changing the extension toolchain.

Inject environment values cleanly
Load environment values by browser and mode, then replace them across JavaScript, JSON, and HTML. Development secrets never leak into production bundles.



Build, preview, package
One flow from local check to store-ready artifacts per browser.
Choose your framework. Keep your workflow.
Start with production-ready templates for React, Preact, Vue, Svelte, TypeScript, WebAssembly, or JavaScript.
React
Component-based UI with full Manifest V3 (MV3) compatibility. Ready-to-ship popup, options, and content scripts.
Preact
Same React API in a 3kB runtime. Great for content scripts where bundle size matters.
Vue
Progressive framework with single-file components, wired up for extension contexts out of the box.
Svelte
Compile-time UI with smaller runtime. Ideal when every kilobyte in a content script counts.
TypeScript
Static types for popup, content, and background scripts. Catch manifest and messaging bugs at compile time.
JavaScript
Plain ES modules, no compile step required. Start fast and add tooling when you need it.
Coming from another framework?
Most React, Preact, 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.
Docs hosted by
Become a sponsor ⏵
Frequently asked questions
What is a browser extension?
What is a browser extension?
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.
Is Extension.js a Chrome extension framework?
Is Extension.js a Chrome extension framework?
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.
How does Extension.js handle hot module replacement during development?
How does Extension.js handle hot module replacement during development?
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.
How do I build one extension for Chrome, Edge, and Firefox?
How do I build one extension for Chrome, Edge, and Firefox?
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.
Can I use React, Preact, Vue, Svelte, or TypeScript?
Can I use React, Preact, Vue, Svelte, or TypeScript?
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.
Does Extension.js support Manifest V3?
Does Extension.js support Manifest V3?
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.
How do I troubleshoot Manifest V3 service worker and permissions issues?
How do I troubleshoot Manifest V3 service worker and permissions issues?
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.
How do I migrate from a custom webpack or Vite setup?
How do I migrate from a custom webpack or Vite setup?
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/Preact/Vue/Svelte/TS code works without rewriting.
Does Extension.js work with monorepos and environment variables?
Does Extension.js work with monorepos and environment variables?
Extension.js supports pnpm, npm, Yarn, and Bun workspaces (any
package.json-based workspaces layout, plus
pnpm-workspace.yaml), and reads deno.jsonc in Deno
projects. 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.
Is Extension.js free to use?
Is Extension.js free to use?
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.
Does Extension.js support Safari?
Does Extension.js support Safari?
Yes. --browser=safari works with build and dev on macOS:
Extension.js converts your extension with Xcode’s
safari-web-extension-converter, builds and signs an app, and opens
it. You enable the extension once in Safari Settings, and from then on
extension logs streams background and content rows and every save
reloads. It requires full Xcode and is macOS-only, and preview and
start have no Safari path. See
Building Safari extensions.
What does Extension.js add to my bundle?
What does Extension.js add to my bundle?
Production builds (extension build) emit only the code your
extension references. The reload and HMR glue is development-only and
is absent from production output. 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.
How do I migrate from another framework?
How do I migrate from another framework?
Most React, Preact, 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.

