npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

webpack-ext-reloader-next

v1.0.2

Published

Webpack extension reloader for Chrome, Firefox, and Edge. Auto-reloads your browser extension on rebuild. MV3-ready successor to webpack-ext-reloader.

Downloads

367

Readme


Why this exists

The original webpack-ext-reloader is unmaintained and broken on Manifest V3 — its WebSocket dies the moment Chrome puts the service worker to sleep, and it doesn't reconnect. This package is the modern successor.

It picks a port, watches your build, and reloads the extension over a self-healing WebSocket that survives service-worker eviction. No configuration required for the common case.

Install

pnpm add -D webpack-ext-reloader-next
# or: npm i -D webpack-ext-reloader-next
# or: yarn add -D webpack-ext-reloader-next

Quick start

Drop one line into your webpack config. Pick the form that matches your setup:

CommonJS (webpack.config.js or .cjs):

const { ExtReloader } = require("webpack-ext-reloader-next");

module.exports = {
  mode: "development",
  // ...your config
  plugins: [new ExtReloader()],
};

ES Modules (webpack.config.mjs, or with "type": "module"):

import { ExtReloader } from "webpack-ext-reloader-next";

export default {
  mode: "development",
  // ...your config
  plugins: [new ExtReloader()],
};

TypeScript (webpack.config.ts):

import { ExtReloader, type ExtReloaderOptions } from "webpack-ext-reloader-next";
import type { Configuration } from "webpack";

const config: Configuration = {
  mode: "development",
  // ...your config
  plugins: [new ExtReloader()],
};

export default config;

The default export is also available if you prefer import ExtReloader from "webpack-ext-reloader-next".

That's it. The plugin reads your manifest.json, infers your background / content / popup entries, and starts a reload server on a free port starting at 9012. Run webpack --watch, load the unpacked build into Chrome, and edit a file — the extension reloads on its own.

The plugin is automatically a no-op when mode === "production".

What it does on each kind of change

| You edited | What happens | |------------|--------------| | manifest.json | Full extension reload (chrome.runtime.reload) | | _locales/** | Full extension reload | | Background service worker | Full extension reload | | Popup / options / devtools page | Reload the open extension page only | | Content script JS | Reload tabs matching the script's matches | | Content script or page CSS | Hot-swap the <link> tag — no reload | | Static asset (image, etc.) | Nothing (just rebuild) |

The CSS hot-swap is the one feature no other webpack extension reloader ships. You edit a content-script stylesheet, the matching <link> is swapped out with a cache-buster, and your Gmail (or whatever) tab keeps its scroll position and form state.

Migrating from webpack-ext-reloader

If you're already using the original webpack-ext-reloader (or webpack-extension-reloader, or @reorx/webpack-ext-reloader), migration is mostly a rename.

1. Swap the dependency:

pnpm remove webpack-ext-reloader
pnpm add -D webpack-ext-reloader-next

2. Update the import — ExtensionReloaderExtReloader:

- const ExtensionReloader = require("webpack-ext-reloader");
+ const { ExtReloader } = require("webpack-ext-reloader-next");

  module.exports = {
    plugins: [
-     new ExtensionReloader({
-       port: 9090,
-       reloadPage: true,
-       entries: {
-         contentScript: "content",
-         background: "background",
-         extensionPage: "popup",
-       },
-     }),
+     new ExtReloader(),
    ],
  };

The entries mapping is no longer required — the plugin reads your manifest.json and infers them. You can still pass entries to override.

3. Delete any manual chrome.runtime.reload() workaround you added because the old plugin's WebSocket died on MV3. The new client reconnects on every Chrome event, so the SW eviction cycle is handled automatically.

Option name compatibility:

| webpack-ext-reloader option | webpack-ext-reloader-next | |-------------------------------|------------------------------| | port | port (default 9012, auto-increments) | | reloadPage | reloadPage (same default) | | entries | entries (now optional) | | manifest | manifest (now optional) | | — | keepAliveInDev (new, default true) | | — | notifications.* (new) | | — | logLevel (new) |

That's the whole migration for the 90% case.

Manifest V3 specifics

  • Service worker reconnection — clients reconnect on every Chrome event so the WebSocket survives the SW eviction cycle.
  • No eval — nothing in the injected client violates the MV3 CSP.
  • Smart keep-alive — opt-in chrome.alarms ping so the SW never sleeps in dev (keepAliveInDev: true, on by default).
  • Cross-browser — Chrome, Firefox, and Edge all supported via the webextension-polyfill types.

Demo

Three MV3 demo extensions live under packages/demo/ — one per target browser. They're identical except for the manifest:

  • packages/demo/chrome — plain background.service_worker. Load via chrome://extensions → "Load unpacked" → packages/demo/chrome/dist.
  • packages/demo/edge — same manifest as Chrome. Load via edge://extensions → "Load unpacked" → packages/demo/edge/dist.
  • packages/demo/firefoxservice_worker + scripts fallback + browser_specific_settings.gecko.id. Load via about:debugging#/runtime/this-firefox → "Load Temporary Add-on" → packages/demo/firefox/dist/manifest.json.
git clone https://github.com/graybearo/webpack-ext-reloader-next
cd webpack-ext-reloader-next
pnpm install
pnpm --filter chrome-demo dev   # or firefox-demo / edge-demo

Firefox MV3 still gates background.service_worker behind the extensions.backgroundServiceWorker.enabled pref, so any Firefox MV3 manifest also needs a background.scripts fallback — that's what the firefox-demo shows.

Standalone CLI (no webpack)

If you build with something other than webpack — esbuild, rollup, a custom script — you can still use the same reload protocol via the CLI. It watches your extension's output directory, classifies changes, and broadcasts to clients you've added to your extension.

npx webpack-ext-reloader-next --dist ./dist

Options:

-p, --port <number>     WebSocket port (default: auto from 9012)
    --manifest <path>   Path to manifest.json (default: probe in --dist)
-d, --dist <path>       Directory to watch (default: ./dist)
-h, --help              Show help

The CLI runs the same diff classifier as the webpack plugin, so reload behavior is identical. You're responsible for getting the client scripts into your extension build (see packages/plugin/src/client/ for the sources).

OS notifications

Optional. If you npm install node-notifier (an optionalDependency), the plugin fires OS-level notifications from the dev server, useful when you're in another window and the build breaks:

  • 'errors' (default) — first error after a streak of successes, and on recovery.
  • 'all' — every reload + every error.
  • false — disabled.
new ExtReloader({ notifications: { osNotifications: "errors" } });

If node-notifier isn't installed (unavailable on some CI environments), the plugin silently skips OS notifications. Everything else works normally.

We deliberately don't ship a chrome.notifications path. It would require consumers to add the "notifications" permission to their manifest just for a dev-time feature, which is poor practice. The action badge, in-page toast, and full-screen error overlay already cover browser-side feedback without permission pollution.

Compared to alternatives

| Feature | this | webpack-ext-reloader | @reorx/webpack-ext-reloader | webpack-extension-reloader-v3-manifest | |---|:---:|:---:|:---:|:---:| | MV3 service worker reconnection | ✅ | ❌ | ⚠️ partial | ⚠️ partial | | Smart routing (per-entry reload) | ✅ | ⚠️ basic | ⚠️ basic | ⚠️ basic | | CSS hot-swap (no reload) | ✅ | ❌ | ❌ | ❌ | | Build-error overlay | ✅ | ❌ | ❌ | ❌ | | Zero-config | ✅ | ❌ | ❌ | ❌ | | Last release | active | 2024 | 2023 | 2023 |

Honest comparison, not marketing — these are all real plugins solving the same problem.

Options

Every option is optional. The defaults work for most projects.

| Option | Type | Default | Notes | |--------|------|---------|-------| | manifest | string | auto | Path to manifest.json. Probes ./manifest.json, ./src/manifest.json, ./public/manifest.json, ./static/manifest.json. | | entries | object | auto | Override entry detection. Shape: { background, contentScript, extensionPage }. | | port | number | 9012 | WebSocket port. Auto-increments if taken. | | maxRetries | number | 50 | How many times the SW retries when the dev server is unreachable. -1 = retry forever. Retries are silent (HTTP-probed before each WS attempt), so a high cap doesn't pollute the console. | | reloadPage | boolean | true | Reload host tabs when content scripts change. | | keepAliveInDev | boolean | true | Use chrome.alarms to keep the MV3 SW alive in dev. | | notifications.toast | boolean | true | Show a small "reloaded" toast in host pages. | | notifications.errorOverlay | boolean | true | Render a full-screen overlay on build errors. | | notifications.osNotifications | 'all' \| 'errors' \| false | 'errors' | OS-level notifications. | | notifications.badge | boolean | true | Color-code the extension's action badge by WS state. | | logLevel | 'silent' \| 'normal' \| 'verbose' | 'normal' | Terminal output verbosity. | | force | boolean | false | Run even when mode === "production". |

Roadmap

v0.1 ships everything below.

  • [x] WebSocket server with auto-port selection (starts at 9012)
  • [x] Resilient client with exponential-backoff reconnection
  • [x] Auto-config: reads manifest.json, no entry mapping required
  • [x] Smart reload classifier (manifest / background / page / tabs / CSS)
  • [x] CSS hot-swap — no reload, no state loss
  • [x] Build-error overlay in content scripts and pages
  • [x] Reload toast on host pages
  • [x] Action-badge state indicator (green / yellow / red)
  • [x] Manifest watcher — picks up changes outside the webpack graph
  • [x] Dual ESM + CJS + TypeScript webpack configs
  • [x] OS notifications (node-notifier as an optional peer dep)
  • [x] Standalone CLI (npx webpack-ext-reloader-next) for non-webpack toolchains

Future:

  • [ ] Multi-compiler support
  • [ ] rspack / vite plugin parity

Contributing

PRs welcome. See CONTRIBUTING.md for setup, commit conventions (we use Conventional Commits + semantic-release, so commit type matters), and the code style.

License

MIT — see LICENSE.