Skip to content


Switch branches/tags

Latest commit


Git stats


Failed to load latest commit information.
Latest commit message
Commit time


npm-version Build Status Coverage Status

Neutrino 9 preset for WebExtension development with hot reload and framework devtools.


  • Zero upfront configuration necessary to start developing and building a WebExtension.
  • Real extension live reloading.
  • Or run development mode in a fake WebExtension environment which supports hot reload and framework devtools.
  • Webpack chunks are translated into manifest configs nicely.
  • Supports code-splitting with native dynamic import(Caveats).
  • Outputs are automatically bundled for each browser respectively, with different manifests.
  • Works well with other official Neutrino presets.

Why Neutrino

Neutrino combines the power of webpack with the simplicity of presets. It is the best scaffolding (AFAIK) on balancing Simplicity and Flexibility.

Who Use It

Five-star extension
with 300k+ users


  • Node.js ^8.10 or 10+
  • Yarn v1.2.1+, or npm v5.4+
  • Neutrino 9
  • webpack 4
  • webpack-cli 3
  • webpack-dev-server 3
  • sinon-chrome >=2 (for fake environment and testing)
  • webextension-polyfill latest (optional, for Chrome)



❯ yarn add --dev neutrino-webextension sinon-chrome webextension-polyfill


❯ npm install --save-dev neutrino-webextension sinon-chrome webextension-polyfill

webextension-polyfill is optional.

Project Layout

This preset follows the standard project layout specified by Neutrino. This means that by default all project source code should live in a directory named src in the root of the project. This includes JavaScript files, CSS stylesheets, images, and any other assets that would be available to your compiled project.

src/manifest/ is the default manifest directory. See explanations below.

├── chrome.manifest.json
├── common.manifest.json
├── firefox.manifest.json
└── [other browser].manifest.json

This preset is designed to work with eslint preset and web preset or other Neutrino presets that are based on eslint or web (e.g. standardjs or react).

This preset should be placed after all other presets. Example:

// .neutrinorc.js

const react = require('@neutrinojs/react')
const webext = require('neutrino-webextension')

module.exports = {
  options: {
    mains: {
      background: {
        entry: 'background',
        webext: {
          type: 'background'
      popup: {
        entry: 'popup',
        webext: {
          type: 'browser_action',
          manifest: {
            browser_style: false,
            default_title: 'My Popup Page'
      content1: {
        entry: 'content1',
        webext: {
          type: 'content_scripts',
          manifest: {
            matches: ['<all_urls>']
      content2: {
        entry: 'content2',
        webext: {
          type: 'content_scripts',
          manifest: {
            matches: [''],
            run_at: 'document_start',
            match_about_blank: true,
            all_frames: true
  use: [
      polyfill: true

This repo itself is also a workable example (But for demo only. Do not use directly in production).

git clone
cd neutrino-webextension
yarn install

yarn start --open-page popup.html
# or
yarn build


Follow instructions of other presets. Typically:

yarn start

Or to jump to a specific entry:

yarn start --open-page [entry name].html

Or just keep one entry for faster bundling:

yarn start --wextentry [entry name]

Debug Mode:

yarn build --debug

Live Reloading:

yarn build --livereload

With webpack watch

yarn build --livereload --debug --watch


Follow instructions of other presets.

This preset should be the last one so that it can duplicate outputs for different browsers.


You can either pack zip files and manually upload via websites or use CLI tools.

Zip & Upload Manually

Add "zip": yarn neutrino-webextension-zip" to package.json scripts.

yarn zip

Zip files for project source and each browser are generated at Neutrino output directory.

Source zip respects .gitignore by default. If you want to add or remove files, pass any number of glob patterns as arguments. Negative patterns must come first, then normal patterns. For example:

yarn neutrino-webextension-zip '!test/specs/**/*' '.env'

CLI Tools

See wext-shipit which is based on web-ext and chrome-webstore-upload-cli.


Use sinon-chrome which supports Chrome and Firefox API stubs.

Preset Options

All options are optional.

  • polyfill: boolean or string. Default false. Generate polyfill related configs. If true the webextension-polyfill should be installed. You can also provide path to a custom polyfill file. If you already import webextension-polyfill in your code, you can set this option to false.
  • removePolyfillSourcemap: boolean. Default true on production. Remove link to source map in webextension-polyfill.
  • manifest: string. Default '<neutrino.options.root>/src/manifest/'. Extension manifests directory.
    • This directory should have at least a [browser].manifest.(json|js) (e.g. firefox.manifest.json). This preset will read the manifest directory to get browser names and generate outputs respectively.
    • A common.manifest.(json|js) can be added for shared values.
    • Version number is copied from package.json by default. If you specify version field on any manifest.json it will overwrite the default. This is not recommended. You should only perform sematic updates on package.json to avoid confusion. See standard-version for example.
  • template: string. Default <neutrino-webextension_root>/template.ejs. Path to a special template for html-webpack-plugin. You normally don't have to change this. The html-webpack-plugin can be configured through web preset or other Neutrino presets that are based on web (e.g react). If you really need to replace the template, copy template.ejs and make your own.
  • setup: string. Default empty. Path to a setup file for development which will run right after the fake WebExtension environment and before running other scripts including webextension-polyfill. Relative path is resolved relative to Neutrino root path. Here you can add other polyfills or play with the sinon-chrome stubs. If you think some of the fake values or functionalities should be added by default, please open a PR to webextensions-emulator.

Entry Options

WebExtension manifest options page, content_scripts, background, pageless, browser_action, page_action, options_page and options_ui SHOULD only be configured in .neutrinorc.js. This preset will handle file dependencies for you. If you manually add these fileds in common.manifest.json or [browser].manifest.json, options may get overwritten.

Specificity: .neutrinorc.js < common.manifest.json < [browser].manifest.json

Options are merged using default deepmerge strategy.

Entry options can be configured through options.mains.[entry].webext. All are optional.

  • webext.type: 'page' | 'pageless' | 'content_scripts' | 'background' | 'browser_action' | 'page_action' | 'options_page' | 'options_ui'. Default 'page'.
    • 'page': Generate normal output. No manifest configuration.
    • 'pageless': Generate normal output without html. No manifest configuration.
    • Others: Generate normal output with or without html. Configure manifest according to the docs.
    • Except 'page', 'pageless' and 'content_scripts' other entry types should be one and only.
    • 'browser_action' and 'page_action' are merged in to 'action' for manifest v3.
  • webext.manifest: object. Other manifest options for this field. See example above.
  • webext.setup: string. Default empty. Path to a setup file which will run right before this entry on development mode. Relative path is resolved relative to Neutrino source path.

Handling multiple presets

By default neutrino will use your preset in .neutrinorc.js. Then in your webpack.config.js you can simply call neutrino preset this way:

module.exports = require('neutrino')().webpack()

Webpack is able to launch multiple builds (for instance if you have two extenions or are building manifest v2 and v3) by exporting an array of builds. Also, you can specify to neutrino the preset object you want to use.

const neutrino = require('neutrino');

const presets1 = require('./neutrino-conf-1.js');
const presets2 = require('./neutrino-conf-2.js');

const build1 = neutrino(presets1).webpack();
const build2 = neutrino(presets2).webpack();

module.exports = [

Migrating from Manifest v2 to v3

Google is pushing the use of Manifest v3 that brings lot of new changes to extensions. Hopefully, neutrino-webextension is compatible.

In this documentation you will be guided through the differences between v2 and v3. We also recommend you to take a look at the migration checklist to make sure you didn't miss any change impacting your extension.

As Firefox is not handling manifest v3 as of today, take a look at the paragraph "Handling multiple presets" to see how to build both manifest version at the same time.

Debug Mode

yarn build --debug
  • Adds process.env.DEBUG variable with true.
  • Adds source map.
  • Removes compression.

Live Reloading

yarn build --livereload

# specify browser application
yarn build --livereload=firefox

# with arguments
yarn build --livereload=google-chrome --livereloadargs='--profile-directory=Profile 1'

Behind the scene whenever webpack finishes bundling, it opens a special url in the browser running the extension. The url is then captured by the injected script which notifies the extension to reload itself.

In order to redirect web requests and reload itself, additional files and permissions are added to the extension. Do not run --livereload in production.

These are added to the manifest:

  • Web requests related permissions.
  • Browsing data permission (for removing special page history).
  • Livereload assets are added to web accessible resources.
  • Background page becomes persistent.

Live reloading is not compatible with Manifest v3.


If you don't use dynamic import skip this section.

Native dynamic import is buggy in Firefox. A workaround is to write a postbuild script targeting only Firefox build. It should collect all the dynamic chunks and append them to every entries in htmls and the manifest.json script lists.

See real-life example.


Neutrino 9 preset for WebExtension/Chrome extension development with hot reload and framework devtools.








No releases published


No packages published

Contributors 4