ECMAScript modules
Author extension code with modern import/export syntax across all extension contexts.
Extension.js supports ECMAScript Modules (ESM) in background scripts, content scripts, extension pages, and regular application modules through its default Rspack pipeline.
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 and explicit dependency boundaries.
Template examples
new

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

Inject script logic into page content while keeping ESM-style authoring.
npx extension@latest create my-extension --template=content
Repository: extension-js/examples/content
Using ESM in existing projects
You can use ESM syntax directly in extension source files (.js, .mjs, .ts, .tsx, etc.) without custom bundler setup.
If your Node-side project files should also run as ESM (for example custom scripts or config conventions), set "type": "module" in package.json:
{
"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 module worker semantics.
- Without module worker type, Extension.js uses classic worker chunk loading behavior.
{
"background": {
"service_worker": "src/background.ts",
"type": "module"
}
}
ESM vs CommonJS reminders
When writing ESM modules:
1) Relative imports should include file extensions in authoring/runtime-aware code paths
// my-file.mjs
import React from 'react'
// local imports
- import myImport from './myImport'
+ import myImport from './myImport.js'
2) Interop with non-ESM modules can differ
When importing CommonJS packages from ESM, prefer documented interop patterns from each package.
3) 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 that should be 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
