This guide collects practical tips for improving the speed of your builds and compilations.
The advice below applies whether you're running build scripts in development or production.
Always use the latest version of webpack. Performance improvements land continuously, so newer releases tend to be faster. You can check the current recommended version here:
Keeping Node.js current helps as well, and so does upgrading your package manager (such as npm or yarn). Recent versions produce more efficient module trees and resolve dependencies faster.
Apply loaders to as few modules as possible. Rather than this:
export default {
// ...
module: {
rules: [
{
test: /\.js$/,
loader: 'babel-loader',
},
],
},
};Use the include field so the loader only runs on the modules that actually need transforming:
import path from 'node:path';
import { fileURLToPath } from 'node:url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
export default {
// ...
module: {
rules: [
{
test: /\.js$/,
include: path.resolve(__dirname, 'src'),
loader: 'babel-loader',
},
],
},
};Every loader and plugin adds to startup time. Keep the number of tools you rely on to a minimum.
You can speed up module resolution with the following changes:
- Keep
resolve.modules,resolve.extensions,resolve.mainFiles, andresolve.descriptionFilesas short as possible, since each entry adds filesystem lookups. - Set
resolve.symlinks: falseif you don't use symlinks (for example,npm linkoryarn link). - Set
resolve.cacheWithContext: falseif you use custom resolving plugins that aren't context specific.
Use the DllPlugin to move infrequently changed code into a separate compilation. This speeds up your application's compilation at the cost of added complexity in the build process.
Reducing the total size of the compilation improves build performance. Try to keep chunks small:
- Use fewer and smaller libraries.
- Use the
SplitChunksPluginin multi-page applications. - Use the
SplitChunksPlugininasyncmode in multi-page applications. - Remove unused code.
- Only compile the part of the codebase you're currently working on.
The thread-loader lets you offload expensive loaders to a pool of workers.
[!WARNING] Don't spawn too many workers, since the Node.js runtime and the loader both have boot overhead. Keep module transfers between the workers and the main process to a minimum, because inter-process communication is expensive.
Enable the cache option in your webpack configuration. Clear the cache directory on "postinstall" in package.json.
[!TIP] Persistent caching supports Yarn PnP version 3 (Yarn 2 Berry).
Profile your own plugins and loaders so they don't become a performance bottleneck.
You can shave time off builds by removing ProgressPlugin from your configuration. Bear in mind that ProgressPlugin offers less value for already-fast builds, so weigh its benefits against the cost.
These steps are especially helpful during development.
Use webpack's watch mode. Avoid third-party tools that watch your files and reinvoke webpack; the built-in watch mode tracks timestamps and forwards that information to the compilation for cache invalidation.
In some setups, watching falls back to polling, which can drive up CPU usage when many files are watched. In those cases, raise the polling interval with watchOptions.poll.
These utilities improve performance by compiling and serving assets from memory instead of writing them to disk:
webpack-dev-serverwebpack-hot-middlewarewebpack-dev-middleware
By default, webpack 4 emits a large amount of data through stats.toJson(). Avoid reading parts of the stats object unless you need them in the incremental step. Starting with v3.1.3, webpack-dev-server included a significant fix that minimizes how much data it pulls from the stats object on each incremental build.
Be aware of the performance trade-offs between the different devtool settings:
"eval"offers the best performance, but doesn't help with transpiled code.- The
cheap-source-mapvariants are faster if you can accept slightly lower-quality mappings. - Use an
eval-source-mapvariant for incremental builds.
[!TIP] In most cases,
eval-cheap-module-source-mapis the best option.
Certain utilities, plugins, and loaders only make sense when building for production. For instance, minifying and mangling your code with the MinimizerPlugin usually isn't worthwhile during development. The following tools are typically best left out in development:
MinimizerPlugin[fullhash],[chunkhash], and[contenthash]AggressiveSplittingPluginAggressiveMergingPluginModuleConcatenationPlugin
webpack only writes updated chunks to the filesystem. For some configuration options — HMR; [name], [chunkhash], or [contenthash] in output.chunkFilename; and [fullhash] — the entry chunk is invalidated alongside the changed chunks.
Keep the entry chunk small so it's cheap to emit. The configuration below extracts the runtime code into its own chunk, making the entry chunk inexpensive to regenerate:
export default {
// ...
optimization: {
runtimeChunk: true,
},
};webpack performs additional algorithmic work to optimize output for size and load performance. These optimizations are worthwhile for smaller codebases, but they can be costly in larger ones:
export default {
// ...
optimization: {
removeAvailableModules: false,
removeEmptyChunks: false,
splitChunks: false,
},
};webpack can include path information in the output bundle, but this adds garbage collection pressure for projects that bundle thousands of modules. Disable it via output.pathinfo:
export default {
// ...
output: {
pathinfo: false,
},
};There was a performance regression in Node.js versions 8.9.10 through 9.11.1 affecting the ES2015 Map and Set implementations. webpack uses these data structures heavily, so the regression slows down compile times. Earlier and later Node.js versions are unaffected.
To improve build times with ts-loader, enable its transpileOnly option. This disables type checking on its own, so restore type checking with the ForkTsCheckerWebpackPlugin, which moves TypeScript type checking and ESLint linting into a separate process.
export default {
// ...
test: /\.tsx?$/,
use: [
{
loader: 'ts-loader',
options: {
transpileOnly: true,
},
},
],
};[!TIP] There's a full example in the
ts-loaderGitHub repository.
These steps are especially helpful for production builds.
[!WARNING] Don't sacrifice the quality of your application for small performance gains. In most cases, the quality of your optimizations matters more than how fast the build runs.
Source maps are genuinely expensive to generate. Make sure you actually need them.
The tools below have known issues that can hurt build performance.
- Keep the number of presets and plugins to a minimum.
- Use
fork-ts-checker-webpack-pluginto run type checking in a separate process. - Configure loaders to skip type checking.
- Run
ts-loaderwithhappyPackMode: trueandtranspileOnly: true.
node-sasshas a bug that blocks threads in the Node.js thread pool. When pairing it withthread-loader, setworkerParallelJobs: 2.