Webpack Bundler Setup for Web Project

Our company is engaged in the development, support and maintenance of sites of any complexity. From simple one-page sites to large-scale cluster systems built on micro services. Experience of developers is confirmed by certificates from vendors.
Development and maintenance of all types of websites:
Informational websites or web applications
Business card websites, landing pages, corporate websites, online catalogs, quizzes, promo websites, blogs, news resources, informational portals, forums, aggregators
E-commerce websites or web applications
Online stores, B2B portals, marketplaces, online exchanges, cashback websites, exchanges, dropshipping platforms, product parsers
Business process management web applications
CRM systems, ERP systems, corporate portals, production management systems, information parsers
Electronic service websites or web applications
Classified ads platforms, online schools, online cinemas, website builders, portals for electronic services, video hosting platforms, thematic portals

These are just some of the technical types of websites we work with, and each of them can have its own specific features and functionality, as well as be customized to meet the specific needs and goals of the client.

Our competencies:
Development stages
Latest works
  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1161
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1041
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    822
  • image_crm_chasseurs_493_0.webp
    CRM development for Chasseurs
    847
  • image_website-sbh_0.png
    Website development for SBH Partners
    999
  • image_website-_0.png
    Website development for Red Pear
    451

Webpack Bundler Configuration for Web Project

Webpack is the most flexible and widely supported bundler for frontend. Supports any stack, any transformations, Module Federation, complex split-chunks strategies. Requires configuration, but behavior is predictable and well-documented.

Used where you need capabilities unavailable in Vite: Module Federation, complex code splitting, non-standard loaders, IE11 support (if still relevant), integration with existing webpack plugins.

What's included in the work

Setting up webpack.config.ts for dev and prod, Babel/SWC, TypeScript, CSS/PostCSS, assets, code splitting, tree shaking, bundle analysis, dev server with HMR.

Installation

npm install -D webpack webpack-cli webpack-dev-server
npm install -D html-webpack-plugin mini-css-extract-plugin css-minimizer-webpack-plugin
npm install -D terser-webpack-plugin compression-webpack-plugin
npm install -D babel-loader @babel/core @babel/preset-env @babel/preset-react @babel/preset-typescript
# or SWC instead of Babel (faster)
npm install -D swc-loader @swc/core @swc/helpers

webpack.config.ts — basic structure

import path from 'path'
import webpack from 'webpack'
import HtmlWebpackPlugin from 'html-webpack-plugin'
import MiniCssExtractPlugin from 'mini-css-extract-plugin'
import CssMinimizerPlugin from 'css-minimizer-webpack-plugin'
import TerserPlugin from 'terser-webpack-plugin'
import CompressionPlugin from 'compression-webpack-plugin'

const isDev = process.env.NODE_ENV !== 'production'
const root = path.resolve(__dirname)

const config: webpack.Configuration = {
  mode: isDev ? 'development' : 'production',

  entry: {
    main: './src/index.tsx',
  },

  output: {
    path: path.resolve(root, 'dist'),
    filename: isDev ? '[name].js' : '[name].[contenthash:8].js',
    chunkFilename: isDev ? '[name].chunk.js' : '[name].[contenthash:8].chunk.js',
    assetModuleFilename: 'assets/[hash][ext][query]',
    publicPath: '/',
    clean: true,
  },

  resolve: {
    extensions: ['.ts', '.tsx', '.js', '.jsx'],
    alias: {
      '@': path.resolve(root, 'src'),
      '@components': path.resolve(root, 'src/components'),
      '@hooks': path.resolve(root, 'src/hooks'),
      '@utils': path.resolve(root, 'src/utils'),
    },
  },

  module: {
    rules: [
      // TypeScript + React via SWC
      {
        test: /\.(ts|tsx|js|jsx)$/,
        exclude: /node_modules/,
        use: {
          loader: 'swc-loader',
          options: {
            jsc: {
              parser: { syntax: 'typescript', tsx: true },
              transform: {
                react: {
                  runtime: 'automatic',
                  development: isDev,
                  refresh: isDev,
                },
              },
              target: 'es2020',
            },
          },
        },
      },
      // CSS + Modules + PostCSS
      {
        test: /\.css$/,
        use: [
          isDev ? 'style-loader' : MiniCssExtractPlugin.loader,
          {
            loader: 'css-loader',
            options: {
              modules: {
                auto: /\.module\.css$/,
                localIdentName: isDev ? '[local]--[hash:base64:5]' : '[hash:base64:8]',
              },
              importLoaders: 1,
            },
          },
          'postcss-loader',
        ],
      },
      // Static assets
      {
        test: /\.(png|jpg|webp|gif|svg)$/,
        type: 'asset',
        parser: {
          dataUrlCondition: { maxSize: 4 * 1024 }, // < 4kb → inline
        },
      },
      {
        test: /\.(woff2?|ttf|eot)$/,
        type: 'asset/resource',
      },
    ],
  },

  plugins: [
    new HtmlWebpackPlugin({
      template: './public/index.html',
      favicon: './public/favicon.ico',
      minify: !isDev,
    }),
    !isDev && new MiniCssExtractPlugin({
      filename: 'css/[name].[contenthash:8].css',
      chunkFilename: 'css/[name].[contenthash:8].chunk.css',
    }),
    new webpack.DefinePlugin({
      'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),
      'process.env.API_URL': JSON.stringify(process.env.API_URL),
    }),
    !isDev && new CompressionPlugin({
      algorithm: 'brotliCompress',
      test: /\.(js|css|html|svg)$/,
      threshold: 10240,
    }),
  ].filter(Boolean),

  optimization: {
    minimize: !isDev,
    minimizer: [
      new TerserPlugin({
        terserOptions: {
          compress: { drop_console: true },
          format: { comments: false },
        },
        extractComments: false,
      }),
      new CssMinimizerPlugin(),
    ],
    splitChunks: {
      chunks: 'all',
      cacheGroups: {
        vendor: {
          test: /[\\/]node_modules[\\/](react|react-dom|react-router-dom)[\\/]/,
          name: 'vendor-react',
          chunks: 'all',
          priority: 20,
        },
        commons: {
          test: /[\\/]node_modules[\\/]/,
          name: 'vendor-commons',
          chunks: 'all',
          priority: 10,
          minChunks: 2,
        },
      },
    },
    runtimeChunk: 'single',
    moduleIds: isDev ? 'named' : 'deterministic',
    chunkIds: isDev ? 'named' : 'deterministic',
  },

  devServer: {
    port: 3000,
    hot: true,
    historyApiFallback: true,
    compress: true,
    proxy: [
      {
        context: ['/api'],
        target: 'http://localhost:8000',
        changeOrigin: true,
      },
    ],
    client: {
      overlay: { errors: true, warnings: false },
    },
  },

  devtool: isDev ? 'eval-cheap-module-source-map' : 'source-map',

  performance: {
    hints: isDev ? false : 'warning',
    maxAssetSize: 250_000,
    maxEntrypointSize: 500_000,
  },
}

export default config

postcss.config.js

module.exports = {
  plugins: [
    require('postcss-import'),
    require('tailwindcss'),
    require('autoprefixer'),
    !process.env.DEV && require('cssnano')({ preset: 'default' }),
  ].filter(Boolean),
}

.swcrc (alternative — separate SWC config file)

{
  "$schema": "https://json.schemastore.org/swcrc",
  "jsc": {
    "parser": {
      "syntax": "typescript",
      "tsx": true,
      "decorators": true
    },
    "transform": {
      "react": {
        "runtime": "automatic"
      },
      "legacyDecorator": true
    },
    "target": "es2020",
    "loose": false,
    "externalHelpers": true
  },
  "module": {
    "type": "es6"
  }
}

Bundle analysis

npm install -D webpack-bundle-analyzer
// add to plugins when ANALYZE=true
process.env.ANALYZE && new BundleAnalyzerPlugin({
  analyzerMode: 'static',
  reportFilename: 'bundle-report.html',
  openAnalyzer: false,
  generateStatsFile: true,
})
ANALYZE=true npm run build
# open dist/bundle-report.html

Lazy loading routes

// src/App.tsx
const ProductsPage = lazy(() =>
  import(/* webpackChunkName: "products" */ './pages/ProductsPage')
)
const CheckoutPage = lazy(() =>
  import(/* webpackChunkName: "checkout" */ './pages/CheckoutPage')
)

// prefetch on idle
const AdminPage = lazy(() =>
  import(/* webpackChunkName: "admin", webpackPrefetch: true */ './pages/AdminPage')
)

TypeScript paths in tsconfig

{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@/*": ["src/*"],
      "@components/*": ["src/components/*"],
      "@hooks/*": ["src/hooks/*"]
    }
  }
}

Paths in tsconfig need to be duplicated in resolve.alias — webpack doesn't read tsconfig automatically.

Environment variables via .env

npm install -D dotenv-webpack
import Dotenv from 'dotenv-webpack'

plugins: [
  new Dotenv({
    path: `.env.${process.env.NODE_ENV}`,
    safe: true, // checks .env.example
    systemvars: true, // env vars from environment have priority
  }),
]

Scripts in package.json

{
  "scripts": {
    "dev": "webpack serve --mode development",
    "build": "webpack --mode production",
    "build:analyze": "ANALYZE=true webpack --mode production",
    "typecheck": "tsc --noEmit"
  }
}

What we do

We configure webpack with SWC (or Babel) for TypeScript + React, CSS Modules, PostCSS, optimize code splitting for specific project, configure dev server with backend proxy, add bundle analyzer, set up brotli compression for production.

Timeline: 1–3 days depending on stack complexity and custom requirements.