文章目录

Building browser extensions has always been one of the most frustrating parts of web development. Between Manifest V3 incompatibilities across Chrome, Edge, and Firefox, hot reload that simply doesn't work for content scripts, and build pipelines that require a degree in webpack configuration — it's no wonder most developers avoid it. extension.js changes that entirely. It's a modern, zero-config framework that lets you build cross-browser extensions using React, Vue, Svelte, or Preact without touching a single webpack config file.

Let me be direct: if you've ever tried to build a browser extension that works across Chrome, Firefox, and Edge, you know the pain. Each browser has its own quirks, Manifest V2 vs V3 differences, and development tools that barely work together. You end up maintaining three separate builds or using a framework like Plasmo or WXT — and even then, there are rough edges.

extension.js solves this with a remarkably clean developer experience. The creator, cezaraugusto, clearly built this from personal frustration. The project is actively maintained with near-daily updates, a Discord community, and responsive issue handling. What's impressive is that it achieves this simplicity without sacrificing capability — HMR works for every script type, TypeScript is first-class, and production builds create store-ready ZIPs automatically.

The key differentiator is the specialized integration system: when you create a project with a React template, extension.js automatically installs the correct React HMR loader without polluting your package.json with framework-specific dev dependencies. This keeps your project clean while still giving you the full hot-reload experience you'd expect from a modern dev server.

extension.js is built on top of rspack (Rust-based webpack alternative) with automatic manifest merging. You write one manifest structure in your manifest.json, and the framework intelligently splits it per browser target. Chrome gets Manifest V3, Firefox gets Manifest V2, and Edge adapts accordingly — all from a single source.

The framework supports four script types natively: background, content, popup, and options. Each has its own hot module replacement, meaning you can edit popup React components and see changes instantly without refreshing the extension. For content scripts, this is particularly valuable since traditional extension dev requires constant reload cycles.

Production builds are similarly thought out: extension build --zip creates a correctly packaged ZIP for Chrome Web Store or Firefox Add-ons submission, with automatic manifest transformations for each store's requirements.

If you're building any of the following, extension.js is worth a serious look:

  • Productivity browser extensions — Tools like tab managers, password helpers, or note-taking overlays that need to work across all three major browsers. The zero-config setup means you can go from idea to working extension in under five minutes.
  • Development utilities — DevTools panels, page analyzers, or API testers that developers install on multiple machines. The TypeScript-first approach makes it natural to build robust tooling.
  • Cross-browser testing assistants — Extensions that help QA testers verify behavior across Chrome, Edge, and Firefox simultaneously.

Here's how to get a working cross-browser extension running in under five minutes:

# Step 1: Create a new project with React template
npx extension@latest create my-extension --template=content-react

# Step 2: Navigate and install dependencies
cd my-extension
npm install

# Step 3: Start development with hot reload
npm run dev

# Step 4: Load the extension in your browser
# Chrome: Extensions page → Load unpacked → select the dist/dev folder
# Firefox: about:debugging → This Firefox → Load Temporary Add-on
# Edge: Extensions page → Enable Developer mode → Load unpacked

# Step 5: Build for production
npm run build

# Step 6: Create store-ready ZIP
extension build --zip

The development server automatically watches all script types and triggers HMR when you save. For popup scripts with React components, changes appear instantly in the popup panel without any manual reload. For content scripts, the framework handles the injection pipeline — you just write React or Vue components as if it were a normal web app.

  • Hot Module Replacement — Works across background, content, popup, and options scripts. Editing any file triggers immediate HMR in the browser without losing extension state. This alone saves hours compared to traditional extension development where every change requires a full reload.
  • Manifest V3 by default with automatic cross-browser adaptation — Write one manifest, get correct builds for Chrome, Edge, and Firefox. The framework handles manifest transformations including icon paths, action definitions, and sidebar actions. Firefox sidebar action icon objects were a known issue that was fixed within days (Issue #449).
  • Zero-config TypeScript and modern frameworks — React, Vue, Svelte, and Preact all work out of the box. The specialized integration system installs framework-specific loaders on-demand, keeping your project dependencies clean. Content script React examples went through several iterations to ensure proper TypeScript and HMR support.

Under the hood, extension.js uses rspack (a Rust-based webpack replacement) which provides significantly faster builds than traditional webpack — especially noticeable when working with larger projects. The framework wraps rspack with its own configuration layer that handles browser-specific manifest merging and script type detection.

The specialized integration system is particularly clever: when you use a template like --template=content-react, the framework detects the framework and installs only the necessary loader (e.g., @rspack/plugin-react-refresh) into the project's node_modules. This approach avoids the common problem where framework-specific dev tools pollute a shared package.json with dependencies that conflict with other tooling.

extension.js has grown to 4,992 Stars on GitHub with active development. The project is maintained by cezaraugusto (1,882 commits) with contributions from OSpoon and others. Recent activity shows near-daily commits with dependency updates across the board — svelte, turbo, rspack, and various loaders all kept current.

The Discord community is active with a dedicated server for discussion. The project has comprehensive documentation at extension.js.org with live templates available at templates.extension.dev so you can try it without installing anything.

If you've heard of or used Plasmo, WXT, or CRXJS, here's how extension.js differentiates:

  • Plasmo is more feature-complete for production use cases, but requires more configuration. extension.js is more opinionated toward simplicity with zero config as a core philosophy. Plasmo also requires a specific SDK setup; extension.js works as a drop-in devDependency.
  • WXT is Vue-focused and has excellent tooling for Vue-based extensions. If you're committed to Vue, WXT might be a better fit. extension.js treats all frameworks equally and has stronger cross-browser Firefox support with Manifest V2 fallback.
  • CRXJS is primarily a build tool for Chrome extensions. It doesn't provide the cross-browser abstraction or specialized framework integrations that extension.js offers.

Issue #421 — Content React Template Build Errors (7 comments)
A developer named Danielku15 reported that the React content script template failed with rspack parse errors on Windows — JSX syntax wasn't being recognized in content scripts. After several back-and-forth exchanges with cezaraugusto across multiple patch versions (3.9.3, 3.9.4, 3.10.0), the root cause turned out to be path resolution differences between macOS and Windows where React loaders expected the library at a specific path. The fix went through three iterations before the developer confirmed success. This kind of cross-platform debugging diligence shows real project maturity. The takeaway for users: always report your OS and Node version when filing bugs.

Issue #449 — Firefox sidebar_action default_icon Object Format Bug (2 comments)
Lebenoa discovered that when firefox:sidebar_action.default_icon was specified as an object (multiple icon sizes) rather than a string, the build failed. Mozilla's manifest specification allows object format for icons but extension.js wasn't handling it. cezaraugusto confirmed and fixed the issue within the same day in version 3.15.0. Fast response like this is a good sign for a project's maintenance quality.

Issue #454 — Returning Values from scripts/ Folder Background Functions
Developer mantou132 raised an interesting question about the scripts/ folder architecture: since scripts must export a default function that returns a cleanup function, there's no standard way to return data values to the calling background script. This is a legitimate architectural consideration — the scripts/ folder follows a specific contract optimized for cleanup rather than data passing. The workaround is using the public/ folder for scripts that need return values, which suggests the framework's design intentionally separates "background scripts with lifecycle management" from "utility scripts with data returns."

  • Windows path resolution for framework loaders: If you hit JSX parse errors on Windows, make sure your Node.js and npm versions are current. The specialized integration system does automatic detection, but older npm versions can cause path resolution issues with rspack's framework loaders.
  • Scripts vs. public folder distinction: The scripts/ folder is for background scripts with automatic lifecycle management (export default returns cleanup). The public/ folder is for scripts that need to return data values. Mixing them up causes unexpected behavior. Read the Special Folders documentation at extension.js.org before structuring your extension.
  • Browser-specific manifest fields: Use the chromium:, firefox:, and edge: prefixes carefully. The framework handles most transformations, but complex configurations like sidebar_action can have edge cases. Always test on all target browsers before publishing.

extension.js is a genuinely useful tool for anyone building browser extensions that need to work across multiple browsers. The zero-config philosophy is more than marketing — it actually delivers a smooth dev experience where you can go from zero to a working cross-browser extension in minutes. The maintainer's responsiveness to issues (even fixing Windows-specific bugs that macOS developers wouldn't catch) is impressive for a project of this scale. If you're tired of wrestling with webpack configs for browser extensions or maintaining separate builds for Chrome, Firefox, and Edge, give extension.js a try — the five-minute quick start actually works as advertised.

Whether you're building a personal productivity tool, a team development utility, or a product you plan to publish to browser stores, extension.js provides a solid foundation that grows with your needs. The specialized integration system is particularly well-thought-out for keeping projects maintainable over time.

Project Links:
GitHub: https://github.com/extension-js/extension.js
Author: https://github.com/cezaraugusto
Documentation: https://extension.js.org

Related: LikeC4 - Developer Tools GitHub Trending Project