| name | chrome-extension-multiple-entry-points |
| description | Build Chrome extensions with multiple entry points using separate Vite configs per script to avoid code-splitting issues. Use when building a Chrome extension with background, content, and popup scripts. |
The Problem
Chrome extensions need multiple entry points (background, content, popup), but modern bundlers create shared chunks that Chrome cannot load across isolated execution contexts.
The Solution
Use a separate Vite config file for each entry point. Each build produces a self-contained IIFE file with no shared chunks.
Project Structure
my-extension/
โโโ src/
โ โโโ background/background.ts
โ โโโ content/content.ts
โ โโโ popup/
โ โ โโโ popup.ts
โ โ โโโ popup.html
โ โ โโโ popup.css
โ โโโ manifest.json
โโโ vite.background.config.ts
โโโ vite.content.config.ts
โโโ vite.popup.config.ts
โโโ package.json
package.json Scripts
{
"scripts": {
"build": "shx rm -rf dist && shx mkdir -p dist && pnpm run build:all && pnpm run copy:assets",
"build:all": "pnpm run build:background && pnpm run build:content && pnpm run build:popup",
"build:background": "vite build --config vite.background.config.ts",
"build:content": "vite build --config vite.content.config.ts",
"build:popup": "vite build --config vite.popup.config.ts",
"copy:assets": "shx cp src/manifest.json src/popup/popup.html src/popup/popup.css dist/",
"dev": "pnpm run build && pnpm run build --watch"
},
"devDependencies": {
"@types/chrome": "^0.0.323",
"typescript": "^5.8.3",
"vite": "^6.3.5",
"shx": "^0.3.4"
}
}
Vite Config Pattern
Each config follows the same pattern โ only the input file and output filename differ:
import { defineConfig } from "vite"
import { resolve } from "path"
export default defineConfig({
build: {
emptyOutDir: false,
rollupOptions: {
input: resolve(__dirname, "src/background/background.ts"),
output: {
entryFileNames: "background.js",
dir: "dist",
format: "iife",
inlineDynamicImports: true,
},
},
target: "es2020",
},
define: {
'process.env': {},
'global': 'globalThis'
},
})
Key Rules
- Use
format: "iife" โ not ES modules
- Set
inlineDynamicImports: true โ no shared chunks
- Use
emptyOutDir: false โ each build appends to dist/, not overwrites
- Use
shx for cross-platform copy/rm commands
- Accept that dependencies are duplicated across bundles โ that is intentional