The ContextReplacementPlugin allows you to override the inferred context of a require.context or dynamic import() expression. This is particularly useful for reducing bundle size by limiting the files webpack includes.
Usage
const webpack = require('webpack');
module.exports = {
plugins: [
new webpack.ContextReplacementPlugin(
/context-directory/,
'./new-directory'
)
]
};
Configuration
Regular expression to match the context resource
Whether to search subdirectories recursively
Regular expression to filter files in the new context
Examples
Limit Moment.js Locales
One of the most common uses - reduce moment.js bundle size:
const webpack = require('webpack');
module.exports = {
plugins: [
// Only include English, French, and Spanish locales
new webpack.ContextReplacementPlugin(
/moment[\\/]locale$/,
/en|fr|es/
)
]
};
This reduces the moment.js bundle from ~500KB to ~150KB.
Limit Angular i18n
new webpack.ContextReplacementPlugin(
/\@angular\b.*\b(bundles|linker)/,
path.resolve(__dirname, 'src')
)
Filter Files in Directory
new webpack.ContextReplacementPlugin(
/\/components$/,
/Button|Input|Select/ // Only include these components
)
Change Context Directory
new webpack.ContextReplacementPlugin(
/old-directory/,
'./new-directory'
)
Disable Recursive Search
new webpack.ContextReplacementPlugin(
/my-directory/,
'./my-directory',
false // Don't search subdirectories
)
With Custom Callback
new webpack.ContextReplacementPlugin(
/context/,
(context) => {
// Modify context properties
context.regExp = /^\.\/.*\.js$/;
context.recursive = false;
}
)
Multiple Locales
const supportedLocales = process.env.LOCALES?.split(',') || ['en'];
const localePattern = new RegExp(supportedLocales.join('|'));
new webpack.ContextReplacementPlugin(
/moment[\\/]locale$/,
localePattern
)
Usage:
LOCALES=en,fr,de npm run build
Filter by File Extension
new webpack.ContextReplacementPlugin(
/\/images$/,
/\.(png|jpe?g|svg)$/ // Only include these image types
)
Advanced Usage
Conditional Replacement
const isDev = process.env.NODE_ENV === 'development';
module.exports = {
plugins: [
isDev
? new webpack.ContextReplacementPlugin(
/components/,
/.*/ // Include all in development
)
: new webpack.ContextReplacementPlugin(
/components/,
/Button|Input/ // Only essential in production
)
]
};
Dynamic Import Context
// Your code with dynamic import
const locale = getUserLocale();
import(`./locales/${locale}.json`);
// Limit which locales are bundled
new webpack.ContextReplacementPlugin(
/\.[\\/]locales$/,
/en|fr|es/
)
Replace with Custom Map
new webpack.ContextReplacementPlugin(
/moment[\\/]locale$/,
'./node_modules/moment/locale',
(context, callback) => {
// Manually specify which files to include
const dependencies = {
'./en': './en',
'./fr': './fr'
};
callback(null, dependencies);
}
)
Use Cases
Reduce Third-Party Library Size
Many libraries include optional files:
// Only include needed Chart.js components
new webpack.ContextReplacementPlugin(
/chart\.js[\\/]dist[\\/]components/,
/BarController|LineController/
)
Locale Management
const moment = require('moment');
// Before optimization: All locales (~500KB)
// After optimization: Only specified locales (~150KB)
new webpack.ContextReplacementPlugin(
/moment[\\/]locale$/,
/(en-gb|fr|de)/
)
Component Library
Only bundle used components:
new webpack.ContextReplacementPlugin(
/@my-company[\\/]components/,
/Button|Input|Modal/ // Only include these
)
Test Files
Exclude test files from production:
if (process.env.NODE_ENV === 'production') {
new webpack.ContextReplacementPlugin(
/src/,
/^(?!.*\.test\.js$).*$/ // Exclude .test.js files
)
}
How It Works
When webpack encounters dynamic imports:
require.context('./locales', true, /\.json$/);
// or
import(`./locales/${name}.json`);
It creates a context module that includes all matching files. The plugin modifies this behavior:
- Matches the context directory with
resourceRegExp
- Applies the new content filter with
newContentRegExp
- Changes the directory with
newContentResource if provided
- Updates recursive setting if specified
Bundle size reduction examples:
- Moment.js locales: 400KB → 150KB (limit to 5 locales)
- Component library: 2MB → 500KB (limit to used components)
- Icon library: 1MB → 100KB (limit to needed icons)
Debugging
Log context information:
new webpack.ContextReplacementPlugin(
/context/,
(context) => {
console.log('Context:', {
resource: context.resource,
regExp: context.regExp,
recursive: context.recursive
});
// Modify context
context.regExp = /new-pattern/;
}
)
Common Patterns
Environment-Specific Files
const env = process.env.NODE_ENV;
new webpack.ContextReplacementPlugin(
/\/config$/,
new RegExp(env) // Only include current environment config
)
Feature Flags
const enabledFeatures = process.env.FEATURES?.split(',') || [];
const pattern = new RegExp(enabledFeatures.join('|'));
new webpack.ContextReplacementPlugin(
/\/features$/,
pattern
)
Changing the context can break imports if the new context doesn’t contain expected files. Always verify the replacement works with your use case.
Comparison with Alternatives
ContextReplacementPlugin
- Modifies what files are included in context
- Reduces bundle size
- Works with dynamic imports
IgnorePlugin
- Completely excludes modules
- No replacement provided
- Good for optional dependencies
NormalModuleReplacementPlugin
- Replaces specific modules
- Not for context/directory replacements
- More precise control
Best Practices
- Test bundle contents after replacement
- Document why replacements are needed
- Use specific patterns to avoid breaking imports
- Monitor bundle size with webpack-bundle-analyzer
- Consider tree-shaking as an alternative for ES modules