Externals

The externals configuration option provides a way of excluding dependencies from the output bundles. Instead, the created bundle relies on that dependency to be present in the consumer's (any end-user application) environment. This feature is typically most useful to library developers, however there are a variety of applications for it.

externals

string object function RegExp [string, object, function, RegExp]

Prevent bundling of certain imported packages and instead retrieve these external dependencies at runtime.

For example, to include jQuery from a CDN instead of bundling it:

index.html

<script
  src="https://code.jquery.com/jquery-3.1.0.js"
  integrity="sha256-slogkvB1K3VOkzAI8QITxV3VzpOnkeNVsKvtkYLMjfk="
  crossorigin="anonymous"
></script>

webpack.config.js

export default {
  // ...
  externals: {
    jquery: "jQuery",
  },
};

This leaves any dependent modules unchanged, i.e. the code shown below will still work:

import $ from "jquery";

$(".my-element").animate(/* ... */);

The property name jquery specified under externals in the above webpack.config.js indicates that the module jquery in import $ from 'jquery' should be excluded from bundling. In order to replace this module, the value jQuery will be used to retrieve a global jQuery variable, as the default external library type is var, see externalsType.

While we showed an example consuming external global variable above, the external can actually be available in any of these forms: global variable, CommonJS, AMD, ES2015 Module, see more in externalsType.

string

Depending on the externalsType, this could be the name of the global variable (see 'global', 'this', 'var', 'window') or the name of the module (see amd, commonjs, module, umd).

You can also use the shortcut syntax if you're defining only 1 external:

export default {
  // ...
  externals: "jquery",
};

equals to

export default {
  // ...
  externals: {
    jquery: "jquery",
  },
};

You can specify the external library type to the external with the ${externalsType} ${libraryName} syntax. It will override the default external library type specified in the externalsType option.

For example, if the external library is a CommonJS module, you can specify

export default {
  // ...
  externals: {
    jquery: "commonjs jquery",
  },
};

[string]

export default {
  // ...
  externals: {
    subtract: ["./math", "subtract"],
  },
};

subtract: ['./math', 'subtract'] allows you select part of a module, where ./math is the module and your bundle only requires the subset under the subtract variable.

When the externalsType is commonjs, this example would translate to require('./math').subtract; while when the externalsType is window, this example would translate to window["./math"]["subtract"];

Similar to the string syntax, you can specify the external library type with the ${externalsType} ${libraryName} syntax, in the first item of the array, for example:

export default {
  // ...
  externals: {
    subtract: ["commonjs ./math", "subtract"],
  },
};

object

export default {
  // ...
  // or
  externals: {
    react: "react",
  },
};
export default {
  // ...
  // or
  externals: {
    lodash: {
      commonjs: "lodash",
      amd: "lodash",
      root: "_", // indicates global variable
    },
  },
};
export default {
  // ...
  // or
  externals: {
    subtract: {
      root: ["math", "subtract"],
    },
  },
};

This syntax is used to describe all the possible ways that an external library can be made available. lodash here is available as lodash under AMD and CommonJS module systems but available as _ in a global variable form. subtract here is available via the property subtract under the global math object (e.g. window['math']['subtract']).

interop

5.109.0+

Non-ESM externals (commonjs, amd, umd, ...) are dynamic modules: importing their default from a strict ES module (a package with "type": "module") yields the whole exports object, while a non-strict importer unboxes it through the runtime __esModule check. The optional interop hint on an object external pins this behavior independent of the importer, mirroring Rollup's output.interop:

  • 'esModule' - treat the external as an ES module namespace, so a default import resolves to its .default export.
  • 'default' - treat the external as a CommonJS module, so a default import resolves to the whole exports object (Node.js semantics).
export default {
  // ...
  externals: {
    dep: {
      amd: "dep",
      interop: "esModule",
    },
  },
};

object with options

5.110.0+

An external value can also be given as an object carrying the target under external plus options describing how webpack should treat it:

export default {
  // ...
  externals: {
    "@scope/icons": {
      external: "commonjs @scope/icons",
      sideEffects: false,
    },
  },
};
  • external - the target, in any of the forms above (a string, an array, or an object per externals type).
  • sideEffects - whether importing the external has side effects, the same idea as the sideEffects flag in a package.json.

webpack cannot analyze an external, so it has to assume that importing one does something observable and keeps the import even when nothing reads its exports. sideEffects: false states the opposite, and lets webpack drop the external entirely when none of its exports are used. This matters most for a large external imported by a barrel file, where the request would otherwise survive into every chunk that touches the barrel.

function

  • function ({ context, request, contextInfo, getResolve }, callback)
  • function ({ context, request, contextInfo, getResolve }) => promise 5.15.0+

It might be useful to define your own function to control the behavior of what you want to externalize from webpack. webpack-node-externals, for example, excludes all modules from the node_modules directory and provides options to allowlist packages.

Here're arguments the function can receive:

  • ctx (object): Object containing details of the file.
    • ctx.context (string): The directory of the file which contains the import.
    • ctx.request (string): The import path being requested.
    • ctx.contextInfo (object): Contains information about the issuer (e.g. the layer and compiler)
    • ctx.getResolve 5.15.0+: Get a resolve function with the current resolver options.
  • callback (function (err, result, type)): Callback function used to indicate how the module should be externalized.

The callback function takes three arguments:

  • err (Error): Used to indicate if there has been an error while externalizing the import. If there is an error, this should be the only parameter used.
  • result (string [string] object): Describes the external module with the other external formats (string, [string], or object)
  • type (string): Optional parameter that indicates the module external type (if it has not already been indicated in the result parameter).

As an example, to externalize all imports where the import path matches a regular expression you could do the following:

webpack.config.js

export default {
  // ...
  externals: [
    function ({ context, request }, callback) {
      if (/^yourregex$/.test(request)) {
        // Externalize to a commonjs module using the request path
        return callback(null, `commonjs ${request}`);
      }

      // Continue without externalizing the import
      callback();
    },
  ],
};

Other examples using different module formats:

webpack.config.js

export default {
  externals: [
    function (ctx, callback) {
      // The external is a `commonjs2` module located in `@scope/library`
      callback(null, "@scope/library", "commonjs2");
    },
  ],
};

webpack.config.js

export default {
  externals: [
    function (ctx, callback) {
      // The external is a global variable called `nameOfGlobal`.
      callback(null, "nameOfGlobal");
    },
  ],
};

webpack.config.js

export default {
  externals: [
    function (ctx, callback) {
      // The external is a named export in the `@scope/library` module.
      callback(null, ["@scope/library", "namedexport"], "commonjs");
    },
  ],
};

webpack.config.js

export default {
  externals: [
    function (ctx, callback) {
      // The external is a UMD module
      callback(null, {
        root: "componentsGlobal",
        commonjs: "@scope/components",
        commonjs2: "@scope/components",
        amd: "components",
      });
    },
  ],
};

RegExp

Every dependency that matches the given regular expression will be excluded from the output bundles.

webpack.config.js

export default {
  // ...
  externals: /^(jquery|\$)$/i,
};

In this case, any dependency named jQuery, capitalized or not, or $ would be externalized.

Combining syntaxes

Sometimes you may want to use a combination of the above syntaxes. This can be done in the following manner:

webpack.config.js

export default {
  // ...
  externals: [
    {
      // String
      react: "react",
      // Object
      lodash: {
        commonjs: "lodash",
        amd: "lodash",
        root: "_", // indicates global variable
      },
      // [string]
      subtract: ["./math", "subtract"],
    },
    // Function
    function ({ context, request }, callback) {
      if (/^yourregex$/.test(request)) {
        return callback(null, `commonjs ${request}`);
      }
      callback();
    },
    // Regex
    /^(jquery|\$)$/i,
  ],
};

For more information on how to use this configuration, please refer to the article on how to author a library.

byLayer

function object

Specify externals by layer.

webpack.config.js

export default {
  externals: {
    byLayer: {
      layer: {
        external1: "var 43",
      },
    },
  },
};

externalsType

string = 'var'

Specify the default type of externals. amd, umd, system and jsonp externals depend on the output.libraryTarget being set to the same value e.g. you can only consume amd externals within an amd library.

Supported types:

webpack.config.js

export default {
  // ...
  externalsType: "promise",
};

externalsType.amd-async

5.109.0+

Specify the default type of externals as 'amd-async'. Like 'amd', the external is resolved through an AMD loader, but it is loaded at runtime via the asynchronous require([...]) API and exposed as an async module. This means the output bundle itself does not need to be wrapped in an AMD library (no matching output.library.type is required), so AMD-only externals can be consumed from any chunk format.

Example

import _ from "lodash";

webpack.config.js

export default {
  // ...
  externalsType: "amd-async",
  externals: {
    lodash: "lodash",
  },
};

The external module resolves to an expression like the following, exposed through webpack's async module runtime:

new Promise((resolve, reject) => {
  if (typeof require !== "function") {
    reject(
      new Error(
        "AMD 'require' is not available to load external module lodash",
      ),
    );
    return;
  }
  require(["lodash"], (module) => resolve(module), reject);
});

externalsType.commonjs

Specify the default type of externals as 'commonjs'. Webpack will generate code like const X = require('...') for externals used in a module.

Example

import fs from "fs-extra";

webpack.config.js

export default {
  // ...
  externalsType: "commonjs",
  externals: {
    "fs-extra": "fs-extra",
  },
};

Will generate into something like:

import fs from "fs-extra";

Note that there will be a require() in the output bundle.

externalsType.global

Specify the default type of externals as 'global'. Webpack will read the external as a global variable on the globalObject.

Example

import jq from "jquery";

jq(".my-element").animate(/* ... */);

webpack.config.js

export default {
  // ...
  externalsType: "global",
  externals: {
    jquery: "$",
  },
  output: {
    globalObject: "global",
  },
};

Will generate into something like

const jq = globalThis.$;

jq(".my-element").animate(/* ... */);

externalsType.module

Specify the default type of externals as 'module'. Webpack will generate code like import * as X from '...' for externals used in a module.

Make sure to enable experiments.outputModule first, otherwise webpack will throw errors.

Example

import jq from "jquery";

jq(".my-element").animate(/* ... */);

webpack.config.js

export default {
  experiments: {
    outputModule: true,
  },
  externalsType: "module",
  externals: {
    jquery: "jquery",
  },
};

Will generate into something like

import * as __WEBPACK_EXTERNAL_MODULE_jquery__ from "jquery";

const jq = __WEBPACK_EXTERNAL_MODULE_jquery__.default;
jq(".my-element").animate(/* ... */);

Note that there will be an import statement in the output bundle.

Preserving phase keywords

5.107.0+

The defer and source import phase keywords are preserved on module externals the same way import attributes are. A static import defer * as ns from "mod" against a module external is emitted as a native import defer * as ... statement, and import source v from "mod" becomes import source ... from "mod". The same external imported with two different phases produces distinct ExternalModule instances, so neither phase is silently dropped.

// input
import defer * as ns from "external-mod";
import source v from "external-mod";

// emitted output (with externalsType: "module")
import defer * as ns from "external-mod";
import source v from "external-mod";

externalsType.import

5.94.0+

Specify the default type of externals as 'import'. Webpack will generate code like import('...') for externals used in a module.

Example

async function foo() {
  const jq = await import("jQuery");
  jq(".my-element").animate(/* ... */);
}

webpack.config.js

export default {
  externalsType: "import",
  externals: {
    jquery: "jquery",
  },
};

Will generate something like below:

const __webpack_modules__ = {
  jQuery: (module) => {
    module.exports = import("jQuery");
  },
};

// webpack runtime...

async function foo() {
  const jq = await Promise.resolve(/* import() */).then(
    __webpack_require__.bind(__webpack_require__, "jQuery"),
  );
  jq(".my-element").animate(/* ... */);
}

Note that the output bundle will have an import() statement.

Preserving phase keywords

5.107.0+

Dynamic import.defer(...) and import.source(...) are also preserved on import externals when the import function name is the default "import". The phase keyword is emitted in the output instead of being stripped.

// input
const ns = await import.defer("external-mod");
const src = await import.source("external-mod");

// emitted output (with externalsType: "import")
const ns = await import.defer("external-mod");
const src = await import.source("external-mod");

externalsType.module-import

5.94.0+

Specify the default type of externals as 'module-import'. This combines 'module' and 'import'. Webpack will automatically detect the type of import syntax, setting it to 'module' for static imports and 'import' for dynamic imports.

Ensure to enable experiments.outputModule first if static imports exist, otherwise, webpack will throw errors.

Example

import { attempt } from "lodash";

async function foo() {
  const jq = await import("jQuery");
  attempt(() => jq(".my-element").animate(/* ... */));
}

webpack.config.js

export default {
  externalsType: "module-import",
  externals: {
    jquery: "jquery",
    lodash: "lodash",
  },
};

Will generate something like below:

import * as __WEBPACK_EXTERNAL_MODULE_lodash__ from "lodash";

const lodash = __WEBPACK_EXTERNAL_MODULE_jquery__;

const __webpack_modules__ = {
  jQuery: (module) => {
    module.exports = import("jQuery");
  },
};

// webpack runtime...

async function foo() {
  const jq = await Promise.resolve(/* import() */).then(
    __webpack_require__.bind(__webpack_require__, "jQuery"),
  );
  (0, lodash.attempt)(() => jq(".my-element").animate(/* ... */));
}

Note that the output bundle will have an import or import() statement.

When a module is not imported via import or import(), webpack will use the "module" externals type as a fallback. If you want to use a different kind of externals as a fallback, you can specify it with a function in the externals option. For example:

export default {
  externalsType: "module-import",
  externals: [
    function ({ request, dependencyType }, callback) {
      if (dependencyType === "commonjs") {
        return callback(null, `node-commonjs ${request}`);
      }
      callback();
    },
  ],
};

externalsType.node-commonjs

Specify the default type of externals as 'node-commonjs'. Webpack will import createRequire from 'module' to construct a require function for loading externals used in a module.

Example

import jq from "jquery";

jq(".my-element").animate(/* ... */);

webpack.config.js

module.export = {
  experiments: {
    outputModule: true,
  },
  externalsType: "node-commonjs",
  externals: {
    jquery: "jquery",
  },
};

Will generate into something like

import { createRequire } from "node:module";

const jq = createRequire(import.meta.url)("jquery");
jq(".my-element").animate(/* ... */);

Note that there will be an import statement in the output bundle.

This is useful when dependencies rely on Node.js built-in modules or require a CommonJS-style require function to preserve prototypes, which is necessary for functions like util.inherits. Refer to this issue for more details.

For code that relies on prototype structures, like:

function ChunkStream() {
  Stream.call(this);
}
util.inherits(ChunkStream, Stream);

You can use node-commonjs to ensure that the prototype chain is preserved:

const { builtinModules } = require("node:module");

export default {
  experiments: { outputModule: true },
  externalsType: "node-commonjs",
  externals: ({ request }, callback) => {
    if (request.startsWith("node:") || builtinModules.includes(request)) {
      return callback(null, `node-commonjs ${request}`);
    }
    callback();
  },
};

This produces something like:

import { createRequire as __WEBPACK_EXTERNAL_createRequire } from "node:module";

const __webpack_modules__ = {
  // ...
  /***/ 2613: /***/ (module) => {
    module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)(
      "stream",
    );

    /***/
  },
  // ...
};

This setup keeps the prototype structure intact, resolving issues with Node.js built-ins.

externalsType.promise

Specify the default type of externals as 'promise'. Webpack will read the external as a global variable (similar to 'var') and await for it.

Example

import jq from "jquery";

jq(".my-element").animate(/* ... */);

webpack.config.js

export default {
  // ...
  externalsType: "promise",
  externals: {
    jquery: "$",
  },
};

Will generate into something like

const jq = await $;

jq(".my-element").animate(/* ... */);

externalsType.self

Specify the default type of externals as 'self'. Webpack will read the external as a global variable on the self object.

Example

import jq from "jquery";

jq(".my-element").animate(/* ... */);

webpack.config.js

export default {
  // ...
  externalsType: "self",
  externals: {
    jquery: "$",
  },
};

Will generate into something like

const jq = globalThis.$;

jq(".my-element").animate(/* ... */);

externalsType.script

Specify the default type of externals as 'script'. Webpack will load the external as a script exposing predefined global variables with HTML <script> element. The <script> tag would be removed after the script has been loaded.

Syntax

export default {
  externalsType: "script",
  externals: {
    packageName: [
      "http://example.com/script.js",
      "global",
      "property",
      "property",
    ], // properties are optional
  },
};

You can also use the shortcut syntax if you're not going to specify any properties:

export default {
  externalsType: "script",
  externals: {
    packageName: "global@http://example.com/script.js", // no properties here
  },
};

Note that output.publicPath won't be added to the provided URL.

Example

Let's load a lodash from CDN:

webpack.config.js

export default {
  // ...
  externalsType: "script",
  externals: {
    lodash: ["https://cdn.jsdelivr.net/npm/lodash@4.17.19/lodash.min.js", "_"],
  },
};

Then use it in code:

import _ from "lodash";

console.log(_.head([1, 2, 3]));

Here's how we specify properties for the above example:

export default {
  // ...
  externalsType: "script",
  externals: {
    lodash: [
      "https://cdn.jsdelivr.net/npm/lodash@4.17.19/lodash.min.js",
      "_",
      "head",
    ],
  },
};

Both local variable head and global window._ will be exposed when you import lodash:

import head from "lodash";

console.log(head([1, 2, 3])); // logs 1 here
console.log(globalThis._.head(["a", "b"])); // logs a here

externalsType.this

Specify the default type of externals as 'this'. Webpack will read the external as a global variable on the this object.

Example

import jq from "jquery";

jq(".my-element").animate(/* ... */);

webpack.config.js

export default {
  // ...
  externalsType: "this",
  externals: {
    jquery: "$",
  },
};

Will generate into something like

const jq = this.$;

jq(".my-element").animate(/* ... */);

externalsType.var

Specify the default type of externals as 'var'. Webpack will read the external as a global variable.

Example

import jq from "jquery";

jq(".my-element").animate(/* ... */);

webpack.config.js

export default {
  // ...
  externalsType: "var",
  externals: {
    jquery: "$",
  },
};

Will generate into something like

const jq = $;

jq(".my-element").animate(/* ... */);

externalsType.window

Specify the default type of externals as 'window'. Webpack will read the external as a global variable on the window object.

Example

import jq from "jquery";

jq(".my-element").animate(/* ... */);

webpack.config.js

export default {
  // ...
  externalsType: "window",
  externals: {
    jquery: "$",
  },
};

Will generate into something like

const jq = globalThis.$;

jq(".my-element").animate(/* ... */);

externalsPresets

object

Enable presets of externals for specific targets.

OptionDescriptionInput Type
electronTreat common electron built-in modules in main and preload context like electron, ipc or shell as external and load them via require() when used.boolean
electronMainTreat electron built-in modules in the main context like app, ipc-main or shell as external and load them via require() when used.boolean
electronPreloadTreat electron built-in modules in the preload context like web-frame, ipc-renderer or shell as external and load them via require() when used.boolean
electronRendererTreat electron built-in modules in the renderer context like web-frame, ipc-renderer or shell as external and load them via require() when used.boolean
bunTreat bun built-in modules like bun, bun:sqlite or bun:ffi, and node.js built-in modules, as external and load them via import when used (for the Bun runtime).boolean
denoTreat node.js built-in modules like fs, path or vm as external and load them via the required node: specifier when used (for the Deno runtime).boolean
nodeTreat node.js built-in modules like fs, path or vm as external and load them via require() when used.boolean
nodeModules5.110.0+ Treat installed packages (requests resolving into a node_modules directory) as external and load them via require()/import at runtime instead of bundling them. See externalsPresets.nodeModules.boolean, object
nwjsTreat NW.js legacy nw.gui module as external and load it via require() when used.boolean
webTreat references to http(s)://... and std:... as external and load them via import when used. (Note that this changes execution order as externals are executed before any other code in the chunk).boolean
webAsyncTreat references to http(s)://... and std:... as external and load them via async import() when used (Note that this external type is an async module, which has various effects on the execution).boolean

Note that if you're going to output ES Modules with those node.js-related presets, webpack will set the default externalsType to node-commonjs which would use createRequire to construct a require function instead of using require().

Example

Using node preset will not bundle built-in modules and treats them as external and loads them via require() when used.

webpack.config.js

export default {
  // ...
  externalsPresets: {
    node: true,
  },
};

externalsPresets.nodeModules

5.110.0+

boolean object

Treat every request that resolves into a node_modules directory as external and load it with require() or import at runtime, instead of bundling it. This is what a server-side build usually wants: the dependencies are already installed next to the output, so bundling them only makes the build slower and the output bigger.

webpack.config.js

export default {
  // ...
  target: "node",
  externalsPresets: {
    nodeModules: true,
  },
};

The preset looks at where the request resolves, not at how it is written, so a request that resolves through a symlink into node_modules (a pnpm store, a linked workspace package) is externalized as well. A few things are never externalized, so you do not have to list them:

  • relative and absolute requests, and # subpath imports, which are never installed packages;
  • anything that does not resolve to a file the runtime can load on its own, that is anything other than .js, .mjs, .cjs, .json and .node, so a package's CSS or assets imported from JavaScript stay bundled and webpack keeps processing them;
  • CSS @import and url() references, which are handled by their own presets;
  • a request that resolve.alias sends to a different package, since the external would keep the original request and load the wrong one.

The external is emitted as node-commonjs, or as module-import when output.module is enabled; a require() dependency stays node-commonjs either way, so its require() semantics are preserved.

externalsPresets.nodeModules.allowlist

Some installed packages still have to be bundled: one that only ships ESM while the output is CommonJS, a workspace package that is not published next to the output, or a package you want processed by your loaders. Pass them in allowlist to keep them bundled:

export default {
  // ...
  externalsPresets: {
    nodeModules: {
      allowlist: [
        // an exact request
        "some-esm-only-package",
        // everything under a scope
        /^@my-company\//,
        // or decide per request
        (request) => request.startsWith("internal-"),
      ],
    },
  },
};

Each entry is a string matched exactly, a RegExp tested against the request, or a function returning true for the requests that should stay bundled.

Edit this page·

19 Contributors

sokraskipjackpksjcefadysamirsadekbyzykzefmanMistyyyyjamesgeorge007tanhauhausnitin315beejunkEugeneHlushkochenxsanpranshuchittorakinetifexanshumanvSaulSilverfi3eworkbjohansebas