Skip to main content
Author extension code with modern import/export syntax across all extension contexts. Extension.js supports ECMAScript Modules (ESM) in background scripts, content scripts, and extension pages. It uses the default Rspack pipeline for module resolution.

When ESM is a good fit

  • You want consistent module syntax across extension and web code.
  • You are sharing code with modern ESM-first packages.
  • You need cleaner tree-shaking (automatic removal of unused code) and explicit dependency boundaries.

Template examples

new

new template screenshot Start a new-tab extension with modern module syntax and minimal setup.
npx extension@latest create my-extension --template=new
Repository: extension-js/examples/new

content

content template screenshot Inject script logic into page content while keeping ESM-style authoring.
npx extension@latest create my-extension --template=content
Repository: extension-js/examples/content

Usage with an existing extension

You can use ESM syntax directly in extension source files (.js, .mjs, .ts, .tsx, etc.) without custom bundler setup. If your Node.js project configuration files should also run as ESM, set "type": "module" in package.json. This applies to custom scripts and configuration conventions:
{
  "name": "my-extension",
  "version": "1.0",
  "description": "My Extension Example",
  "type": "module",
  "devDependencies": {
    "extension": "latest"
  },
  "scripts": {
    "dev": "extension dev",
    "start": "extension start",
    "build": "extension build"
  }
}

Manifest and service worker notes

For Manifest V3 background workers:
  • Set background.type to "module" when you want native ES module loading in the service worker.
  • Without module worker type, Extension.js uses classic worker loading (bundled scripts without native module syntax).
{
  "background": {
    "service_worker": "src/background.ts",
    "type": "module"
  }
}

ESM vs CommonJS reminders

When writing ESM modules:

Include file extensions in relative imports

// my-file.mjs
import React from 'react'

// local imports
- import myImport from './myImport'
+ import myImport from './myImport.js'

Interoperability with non-ESM modules can differ

When importing CommonJS packages from ESM, follow the compatibility guidance each package provides.

CommonJS globals are not available in strict ESM contexts

Avoid relying on require, module.exports, __filename, and __dirname inside ESM modules.

Handling environment variables in ECMAScript modules

Extension.js supports both:
  • process.env.EXTENSION_PUBLIC_*
  • import.meta.env.EXTENSION_PUBLIC_*
Use EXTENSION_PUBLIC_ for variables you want available in extension code.
// example.mjs

console.log(import.meta.env.EXTENSION_PUBLIC_API_KEY)
console.log(process.env.EXTENSION_PUBLIC_API_KEY)

Next steps

Video walkthrough