A good development setup improves productivity with fast rebuilds, hot reloading, and helpful debugging tools.
Development Mode
Set mode to development for better debugging and faster builds:
module.exports = {
mode: 'development'
};
This automatically enables:
- Named module IDs for better debugging
- Faster builds without minification
- Useful development plugins
- Better error messages
Development mode optimizes for build speed and debugging experience, not bundle size.
Webpack Dev Server
The dev server provides live reloading and hot module replacement.
Install webpack-dev-server
npm install --save-dev webpack-dev-server
Configure dev server
const path = require('path');
module.exports = {
mode: 'development',
devServer: {
static: {
directory: path.join(__dirname, 'public')
},
port: 3000,
hot: true,
open: true,
compress: true,
historyApiFallback: true
}
};
Add dev script
In package.json:{
"scripts": {
"dev": "webpack serve --config webpack.config.js"
}
}
Source Maps
Source maps help debug bundled code by mapping it back to the original source.
Recommended for Development
module.exports = {
devtool: 'eval-source-map' // Fast rebuilds with good quality
};
Source Map Options
| Option | Build Speed | Rebuild Speed | Quality | Production |
|---|
eval | +++ | +++ | - | No |
eval-source-map | — | + | +++ | No |
cheap-module-source-map | + | ++ | ++ | No |
source-map | — | — | +++ | Yes |
hidden-source-map | — | — | +++ | Yes |
For the fastest rebuilds, use eval-source-map. For the best quality debugging, use source-map.
Hot Module Replacement (HMR)
HMR updates modules without a full page reload:
const webpack = require('webpack');
module.exports = {
devServer: {
hot: true
},
plugins: [
new webpack.HotModuleReplacementPlugin()
]
};
Enable HMR in Code
// main.js
import { render } from './app';
render();
if (module.hot) {
module.hot.accept('./app', () => {
render();
});
}
Most frameworks (React, Vue) have HMR support built into their loaders. You usually don’t need to write HMR code manually.
Lazy Compilation
Compile dynamic imports only when they’re requested:
module.exports = {
mode: 'development',
experiments: {
lazyCompilation: true
},
cache: {
type: 'filesystem'
}
};
Lazy compilation can dramatically speed up development server start time for large applications with many routes or dynamic imports.
Watch Mode
Automatic rebuilds on file changes:
module.exports = {
watch: true,
watchOptions: {
ignored: /node_modules/,
aggregateTimeout: 300,
poll: 1000 // Enable polling for certain file systems
}
};
Or use the CLI:
Dev Server Configuration
Proxy API Requests
module.exports = {
devServer: {
proxy: [
{
context: ['/api'],
target: 'http://localhost:8080',
changeOrigin: true,
pathRewrite: { '^/api': '' }
}
]
}
};
HTTPS Support
module.exports = {
devServer: {
https: true,
// Or with custom certificates
https: {
key: fs.readFileSync('/path/to/server.key'),
cert: fs.readFileSync('/path/to/server.crt')
}
}
};
module.exports = {
devServer: {
headers: {
'X-Custom-Header': 'value',
'Access-Control-Allow-Origin': '*'
}
}
};
Filesystem Caching
Dramatically speed up rebuilds:
const path = require('path');
module.exports = {
cache: {
type: 'filesystem',
cacheDirectory: path.resolve(__dirname, '.cache'),
buildDependencies: {
config: [__filename]
}
},
infrastructureLogging: {
level: 'verbose' // See cache activity
}
};
Filesystem caching is enabled by default in development mode and can reduce rebuild times by 90%+.
Error Handling
Better Error Messages
module.exports = {
stats: 'errors-warnings',
infrastructureLogging: {
level: 'error'
}
};
Error Overlay
module.exports = {
devServer: {
client: {
overlay: {
errors: true,
warnings: false
}
}
}
};
Complete Development Config
Here’s a complete development configuration:
const path = require('path');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
mode: 'development',
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].js',
clean: true
},
devtool: 'eval-source-map',
devServer: {
static: {
directory: path.join(__dirname, 'public')
},
port: 3000,
hot: true,
open: true,
compress: true,
historyApiFallback: true,
client: {
overlay: {
errors: true,
warnings: false
}
},
proxy: [
{
context: ['/api'],
target: 'http://localhost:8080',
changeOrigin: true
}
]
},
cache: {
type: 'filesystem'
},
experiments: {
lazyCompilation: true
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
cacheDirectory: true
}
}
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
},
{
test: /\.(png|svg|jpg|jpeg|gif)$/,
type: 'asset/resource'
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: './public/index.html'
}),
new webpack.HotModuleReplacementPlugin()
],
stats: 'errors-warnings',
infrastructureLogging: {
level: 'warn'
}
};
Environment Variables
Use different values for development:
const webpack = require('webpack');
module.exports = {
plugins: [
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify('development'),
'process.env.API_URL': JSON.stringify('http://localhost:8080')
})
]
};
Or use dotenv:
npm install --save-dev dotenv-webpack
const Dotenv = require('dotenv-webpack');
module.exports = {
plugins: [
new Dotenv({
path: './.env.development'
})
]
};
Skip Type Checking in Dev
For TypeScript, skip type checking during builds:
module.exports = {
module: {
rules: [
{
test: /\.tsx?$/,
loader: 'ts-loader',
options: {
transpileOnly: true // Skip type checking
}
}
]
}
};
Run type checking separately:
{
"scripts": {
"dev": "webpack serve",
"type-check": "tsc --noEmit",
"type-check:watch": "tsc --noEmit --watch"
}
}
Reduce Plugins in Development
Only use essential plugins during development:
const isProduction = process.env.NODE_ENV === 'production';
module.exports = {
plugins: [
new HtmlWebpackPlugin(),
// Only in production
...(isProduction ? [
new MiniCssExtractPlugin(),
new CompressionPlugin()
] : [])
]
};
Don’t use minification, compression, or complex optimizations in development - they slow down builds significantly.