Learn-skills.dev webpack-vite
Frontend bundler configuration for Webpack and Vite. Use when user mentions "webpack", "vite", "bundler", "vite config", "webpack config", "code splitting", "tree shaking", "hot module replacement", "HMR", "build optimization", "bundle size", "chunk splitting", "loader", "plugin", "esbuild", "rollup", "dev server", or configuring JavaScript build tools.
install
source · Clone the upstream repo
git clone https://github.com/NeverSight/learn-skills.dev
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/NeverSight/learn-skills.dev "$T" && mkdir -p ~/.claude/skills && cp -r "$T/data/skills-md/1mangesh1/dev-skills-collection/webpack-vite" ~/.claude/skills/neversight-learn-skills-dev-webpack-vite && rm -rf "$T"
manifest:
data/skills-md/1mangesh1/dev-skills-collection/webpack-vite/SKILL.mdsource content
Webpack and Vite Reference
Vite Configuration
// vite.config.ts import { defineConfig } from 'vite'; import react from '@vitejs/plugin-react'; import path from 'path'; export default defineConfig({ plugins: [react()], resolve: { alias: { '@': path.resolve(__dirname, './src'), '@components': path.resolve(__dirname, './src/components'), }, }, server: { port: 3000, open: true, strictPort: true }, build: { outDir: 'dist', sourcemap: true, target: 'esnext', minify: 'esbuild', // or 'terser' for drop_console rollupOptions: { output: { manualChunks: { vendor: ['react', 'react-dom'], router: ['react-router-dom'] }, }, }, }, optimizeDeps: { include: ['lodash-es', 'axios'], // force pre-bundling exclude: ['your-local-package'], }, css: { modules: { localsConvention: 'camelCaseOnly' }, preprocessorOptions: { scss: { additionalData: `@use "@/styles/variables" as *;` } }, }, });
Vite Environment Variables
# .env / .env.local / .env.development / .env.production / .env.[mode] # Only VITE_ prefixed vars are exposed to client code VITE_API_URL=https://api.example.com DB_PASSWORD=secret # NOT exposed to client
const apiUrl = import.meta.env.VITE_API_URL; const isDev = import.meta.env.DEV; // boolean const mode = import.meta.env.MODE; // 'development' | 'production' | custom // Type declarations (env.d.ts) /// <reference types="vite/client" /> interface ImportMetaEnv { readonly VITE_API_URL: string }
Vite Plugins
import react from '@vitejs/plugin-react'; // Babel-based import reactSWC from '@vitejs/plugin-react-swc'; // SWC-based (faster) import vue from '@vitejs/plugin-vue'; import svgr from 'vite-plugin-svgr'; // SVG as React components import { VitePWA } from 'vite-plugin-pwa'; import legacy from '@vitejs/plugin-legacy'; // IE11 / older browser support import { visualizer } from 'rollup-plugin-visualizer'; // bundle analysis export default defineConfig({ plugins: [ reactSWC(), svgr(), VitePWA({ registerType: 'autoUpdate', workbox: { globPatterns: ['**/*.{js,css,html,ico,png,svg}'] } }), legacy({ targets: ['defaults', 'not IE 11'] }), visualizer({ open: true, gzipSize: true, brotliSize: true }), ], });
Vite Dev Server and Proxy
export default defineConfig({ server: { proxy: { '/api': { target: 'http://localhost:4000', changeOrigin: true, rewrite: (p) => p.replace(/^\/api/, '') }, '/ws': { target: 'ws://localhost:4000', ws: true }, }, https: { key: './certs/key.pem', cert: './certs/cert.pem' }, cors: true, fs: { allow: ['../..'] }, // monorepo: serve files outside root }, });
Webpack 5 Configuration
const path = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); module.exports = { mode: 'production', entry: { main: './src/index.tsx', admin: './src/admin.tsx' }, output: { path: path.resolve(__dirname, 'dist'), filename: '[name].[contenthash].js', chunkFilename: '[name].[contenthash].chunk.js', clean: true, publicPath: '/', }, resolve: { extensions: ['.tsx', '.ts', '.js', '.jsx'], alias: { '@': path.resolve(__dirname, 'src') }, }, module: { rules: [ { test: /\.(ts|tsx|js|jsx)$/, exclude: /node_modules/, use: { loader: 'babel-loader', options: { presets: ['@babel/preset-env', '@babel/preset-react', '@babel/preset-typescript'] } } }, { test: /\.css$/, use: [MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader'] }, { test: /\.module\.css$/, use: [MiniCssExtractPlugin.loader, { loader: 'css-loader', options: { modules: true } }, 'postcss-loader'] }, { test: /\.s[ac]ss$/, use: [MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader', 'sass-loader'] }, { test: /\.(png|jpg|gif|svg|webp)$/, type: 'asset', parser: { dataUrlCondition: { maxSize: 8 * 1024 } } }, { test: /\.(woff|woff2|eot|ttf|otf)$/, type: 'asset/resource' }, ], }, plugins: [ new HtmlWebpackPlugin({ template: './public/index.html', minify: { collapseWhitespace: true } }), new MiniCssExtractPlugin({ filename: 'css/[name].[contenthash].css' }), ], }; // Note: Webpack 5 asset modules replace file-loader, url-loader, and raw-loader.
Webpack Plugins
const { DefinePlugin, ProvidePlugin } = require('webpack'); const CopyWebpackPlugin = require('copy-webpack-plugin'); const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer'); module.exports = { plugins: [ new DefinePlugin({ 'process.env.API_URL': JSON.stringify(process.env.API_URL), __DEV__: JSON.stringify(process.env.NODE_ENV === 'development'), }), new ProvidePlugin({ React: 'react' }), new CopyWebpackPlugin({ patterns: [{ from: 'public/assets', to: 'assets' }] }), new BundleAnalyzerPlugin({ analyzerMode: 'static', openAnalyzer: false }), ], };
Code Splitting
// Dynamic import -- creates a separate chunk loaded on demand const LazyComponent = React.lazy(() => import('./HeavyComponent')); const Admin = React.lazy(() => import(/* webpackChunkName: "admin" */ './AdminPanel')); // Webpack splitChunks module.exports = { optimization: { splitChunks: { chunks: 'all', maxInitialRequests: 20, minSize: 20000, cacheGroups: { vendor: { test: /[\\/]node_modules[\\/]/, name: 'vendors', priority: -10 }, commons: { minChunks: 2, priority: -20, reuseExistingChunk: true }, }, }, runtimeChunk: 'single', }, }; // Vite manual chunks export default defineConfig({ build: { rollupOptions: { output: { manualChunks(id) { if (id.includes('node_modules')) { if (id.includes('react')) return 'react-vendor'; return 'vendor'; } }, }, }, }, });
Tree Shaking
// package.json -- mark as side-effect-free for tree shaking { "sideEffects": false } // Or specify files with side effects: { "sideEffects": ["*.css", "*.scss", "./src/polyfills.ts"] }
import { debounce } from 'lodash-es'; // tree-shakeable (ESM) const { debounce } = require('lodash'); // NOT tree-shakeable (CJS), bundles everything // Webpack: set mode 'production' to enable terser + dead code elimination // Vite: tree shaking via Rollup is on by default in production
Bundle Analysis
# Webpack npx webpack --profile --json > stats.json && npx webpack-bundle-analyzer stats.json # Or use BundleAnalyzerPlugin (see Webpack Plugins section) # Vite -- use rollup-plugin-visualizer (see Vite Plugins section)
Webpack Dev Server
module.exports = { devServer: { port: 3000, hot: true, historyApiFallback: true, // SPA routing fallback compress: true, proxy: [{ context: ['/api'], target: 'http://localhost:4000', changeOrigin: true, pathRewrite: { '^/api': '' } }], https: true, static: { directory: path.join(__dirname, 'public') }, }, };
Production Optimization
// Webpack const CssMinimizerPlugin = require('css-minimizer-webpack-plugin'); const TerserPlugin = require('terser-webpack-plugin'); const CompressionPlugin = require('compression-webpack-plugin'); module.exports = { mode: 'production', devtool: 'source-map', // 'hidden-source-map' to hide from users optimization: { minimize: true, minimizer: [ new TerserPlugin({ terserOptions: { compress: { drop_console: true, drop_debugger: true } } }), new CssMinimizerPlugin(), ], }, plugins: [ new CompressionPlugin({ algorithm: 'gzip', test: /\.(js|css|html|svg)$/, threshold: 10240 }), ], };
// Vite production export default defineConfig({ build: { sourcemap: 'hidden', minify: 'terser', terserOptions: { compress: { drop_console: true } }, cssCodeSplit: true, assetsInlineLimit: 4096, // inline assets < 4KB as base64 chunkSizeWarningLimit: 500, }, });
CSS Handling
// postcss.config.js -- shared by both Webpack and Vite module.exports = { plugins: { 'tailwindcss': {}, 'autoprefixer': {}, 'cssnano': process.env.NODE_ENV === 'production' ? {} : false, }, }; // Vite: install sass and import .scss directly -- no loader config needed // Webpack: add sass-loader to module.rules (see Webpack 5 Configuration)
Migration from Webpack to Vite
npm install -D vite @vitejs/plugin-react # 1. install # 2. Create vite.config.ts # see "Vite Configuration" section # 3. Move index.html to project root, add: <script type="module" src="/src/main.tsx"></script> # 4. Update package.json scripts: "dev": "vite", "build": "tsc && vite build", "preview": "vite preview"
| Webpack | Vite |
|---|---|
/ | / (ESM only) |
| |
/ | Native static asset handling |
| option in vite.config.ts |
comments | |
| |
| webpack-dev-server proxy | in vite config |
Monorepo Bundling
// Vite monorepo export default defineConfig({ resolve: { alias: { '@shared/ui': path.resolve(__dirname, '../../packages/ui/src') } }, optimizeDeps: { include: ['@shared/ui'] }, server: { fs: { allow: ['../..'] } }, }); // Webpack monorepo module.exports = { resolve: { alias: { '@shared/ui': path.resolve(__dirname, '../../packages/ui/src') }, symlinks: false }, module: { rules: [{ test: /\.(ts|tsx)$/, include: [path.resolve(__dirname, 'src'), path.resolve(__dirname, '../../packages')], use: 'babel-loader', }] }, };
Performance Budgets
// Webpack -- built-in performance hints module.exports = { performance: { hints: 'error', // 'warning' | 'error' | false maxAssetSize: 250000, // 250KB per asset maxEntrypointSize: 400000, // 400KB per entry point assetFilter: (file) => !/\.map$/.test(file), }, };
# size-limit -- CI-friendly, works with both Webpack and Vite npm install -D size-limit @size-limit/preset-app # package.json: "size-limit": [{ "path": "dist/assets/*.js", "limit": "200 KB", "gzip": true }] npx size-limit