Quick start
This walkthrough gets a working extension running in Chrome in about 60 seconds. You need Node 20+, pnpm, and Chrome installed.
1. Scaffold
Section titled “1. Scaffold”Run the scaffold command with --defaults to skip the interactive prompts:
pnpm dlx extforge init my-extension --defaultsDefaults: React, Tailwind CSS, Chrome + Firefox, popup + background service worker, storage + activeTab permissions.
The command creates a my-extension/ directory with a complete project structure.
2. Install dependencies
Section titled “2. Install dependencies”cd my-extension && pnpm installThis installs extforge, TypeScript, esbuild, React, Tailwind, and Vitest as dev dependencies.
3. Run the dev server
Section titled “3. Run the dev server”pnpm devYou will see output like:
extforge › dev ✔ Built chrome in 312ms ✔ Built firefox in 287ms ⚡ HMR server listening on ws://localhost:9100 📂 dist/chrome/ 📂 dist/firefox/The bundler runs esbuild for each target browser in parallel, then starts a WebSocket server on port 9100 for HMR. Both build outputs are ready immediately.
4. Load the unpacked extension
Section titled “4. Load the unpacked extension”- Open
chrome://extensionsin Chrome. - Enable Developer mode (toggle, top-right).
- Click Load unpacked and select the
dist/chrome/path printed in your terminal.
The extension installs. You will see its icon in the Chrome toolbar. Click it to open the popup.
5. Edit and watch HMR
Section titled “5. Edit and watch HMR”Open src/ui/popup/index.tsx in your editor. Find the string "Your extension is running!" and change it to something else. Save the file.
Within a second, the popup content updates — no manual reload, no reopening the extensions page. ExtForge’s HMR layer sends a targeted reload message over the WebSocket connection, and the popup frame refreshes in place.
6. What just happened
Section titled “6. What just happened”When you saved the file, esbuild re-bundled only the changed module (typically under 50ms). The HMR WebSocket server broadcast a reload event to the extension’s background service worker, which used chrome.runtime.reload() scoped to the changed script ID. The popup re-mounted with the new bundle — no full extension reload required.
The pieces involved: the esbuild incremental bundle, the WebSocket server on port 9100 with infinite reconnect logic, and script-id targeting in the HMR client. See HMR in depth for how to configure reload strategies and handle edge cases.
7. Next steps
Section titled “7. Next steps”- Configuration guide — customise
extforge.config.ts, change browsers, add plugins. - Plugins guide — extend the build pipeline with first-party and custom plugins.
- Testing guide — unit tests with Chrome API fakes, Playwright e2e fixtures.