Saturday, October 4, 2025

Webpack Tutorial

What is webpack?

Webpack is a build tool or module bundler designed to streamline the development and deployment of modern web applications.
It is used in apps where we have a reach and SPA. reach means lots of dynamic application.

How apps were build w/o webpack?

Challenges-
Let we have an app with large js files e.g. main.js, external.js, payment.js

  1. debugging is difficult
  2. working on code changes will be hard
  3. failure impact is more (due to interdependency and large files)
  4. scoping issues (variables with same name in different files raises the conflicts)

To avoid these issues we use below approaches.

Approach 1:

JS files with small size/features files.
In this approach

  1. debugging is easier
  2. working on files was easy
  3. no scoping issues
    Challenges -
  4. order of execution is important
  5. harder to manage
  6. network load increases - due to multiple n/w call for each js file

Approach 2:
Webpack approach

  • It takes the first entry file e.g. index.js.
  • It combines all the files into one JS file.
  • It minimizes, optimize, compress the file and new file is less in size. and creates a bundle.js file.

Approach 3:
Traditional server side approach- Single page app (SPA)

It has Client and Server architecture where it works on request and response.
where when client requests, the server sends a minimal html file and client's browser has js code that helps in the execution of the tasks.

Challenges
I. Large JavaScript code on client with multiple dependencies
2. We need to breakdown js files and minify for faster load
3. We need polyfills to work application on old browsers

Webpack approach

Provide initial file, then we go through all the dependencies and create the dependency graph with dependency hierarchy. Then it create final bundle file. It could be single or multiple file based on your configuration then converts ts to js file then it minimizes, optimize, compress the file. also converts the necessary pre-processor css into css and moves the images/ assets into the final bundle. and this bundle code has all the code for running the application.

All the above approaches has different challenges and to help resolve we have webpack.

How webpack works?
Step 1: Webpack expects one or more entry file/ points possible (ENTRY)
then using it will create the dependency graph
Step 2: Required different kind of (LOADERS) to handle different file type.e.g. images, fonts etc.
Step 3: Then require different (PLUGINS) to perform global transformations.
Step 4: We will have (OUTPUT) with optimized bundle.

What webpack can do?

  1. Load different assets
  2. Dependency graph
  3. Optimized production build
  4. Bundle splitting
  5. Hot module replacement
    G. Dead code elimination (Tree shaking)
  6. Module federation
  7. Caching
  8. Duplicate code elimination

Key Features of Webpack

Code Splitting: Divides your application into smaller bundles, improving loading efficiency.
Loaders: Transforms non-JavaScript files (e.g., CSS, TypeScript) into modules that Webpack can process.
Plugins: Extends functionality, such as optimizing assets, minifying files, or defining environment variables.
Hot Module Replacement (HMR): Enables real-time updates without reloading the page, enhancing development speed.
Tree Shaking: Removes unused code, reducing bundle size and improving performance.

Steps to work on Webpack

npm init
npm install webpack webpack-cli --save-dev (instaling as dev dependency)
npm install --save-dev babel-loader @babel/core @babel/preset-env
create a file .babelrc or babel.config.json, then add
{
"presets": ["@babel/preset-env"]
}

Node.js common js modules
Index.js
const sum = require('/sum")

sum.js
module.exports = sum

ES6 modules
Index.js
import sum from "./sum"

sum.js
export default sum

configuration

we will create webpack.config.js file and export the configuration.
const path = require("path");
module.exports = {
// we specify config. here
// as a config. 2 imp prop are there and that is requird. for minimum config.
// one is entry and other is output. i.e our output directory where our final //output file will be placed.

entry: "./index.js",
output: { // it is object
    filename: "bundle.js",
    path: path.resolve(_dirname, "dist")
},

// Add this for babel
module: {
rules: [
  {
    test: /\.(js|jsx)$/,   // process .js and .jsx
    exclude: /node_modules/,
    use: {
      loader: "babel-loader",
      options: {
        presets: [
          "@babel/preset-env",
          "@babel/preset-react" // if using React
        ]
      }
    }
  }
]

},
resolve: {
extensions: [".js", ".jsx"] // so you can import without extensions
}
}

here it will generate the dependency graph and also bundle.js file in dist folder.
Note that in case of production and development we will have different bundle.js file with different code.

What is Babel?

Babel is a JavaScript compiler and toolchain designed to transform modern JavaScript code (ES2015+ and beyond) into a version that can run in older browsers or environments. By converting new syntax into compatible JavaScript, Babel allows developers to use the latest features without waiting for all browsers to catch up.

Key Functions of Babel
Syntax Transformation: Babel transforms newer JavaScript syntax into a version that older browsers can understand.
Polyfilling Features: Babel can add missing features to older browsers through polyfills.
Source Code Transformations: Babel allows developers to perform transformations on their code, often referred to as codemods. These transformations can help in migrating codebases to newer versions of JavaScript or applying best practices.

Using CSS, Images, Fonts

Webpack don't know what to do with files like images, fonts etc. It only knows javascript
It does pre-processing of different file types before adding them into the final bundle.
Loaders can do--

  1. Transpilation (e.g. ts to js conversion easily understandable by browser)- babel is doing it for us
  2. Managing the images.
  3. Handling different fonts and css.

For css
npm install css-loader style-loader --save-dev
{
test: /.(css)$/,
use:["style-loader", "css-loader"]
}

For SCSS
npm install sass-loader sass --save-dev
test: /.(scss)$/,
use:["style-loader", "css-loader", "sass-loader"]
}

if we are using multiple css files then avoid we need to use single css discussed later.

plugins

Plugins are the backbone of webpack. it serves the purpose of anything that is not possible for the loaders.

  • It is JS library or class which add more functionality to webpack.
  • Plugins work at global level i.e. bundle (bundle folder) or chunk level (chunk means dividing the output into multiple files).
  • Generally works at the end of bundle generation process. (works just before the output)

Dependency graph

The Webpack Dependency Graph is a core concept in Webpack that represents the relationships between modules in your application. It is a directed graph where each node is a module, and edges represent dependencies between these modules. This graph allows Webpack to bundle all necessary modules efficiently.

Webpack starts from one or more entry points specified in its configuration. It then recursively analyzes each module to identify its dependencies. These dependencies can include JavaScript files, stylesheets, images, fonts, or other assets. Webpack builds a graph of all these interconnected modules and bundles them into one or more output files.

Issues with bundling is that when we create a bundle we don't have all the images and bundles and some other files so to resolve this we need to use plugins. Bundles should be optimized also.

[Progress]
ProgressPlugin - tracks progress of build
[Bundle] - bundle should contain all the file
HtmlWebpackPlugin -
mini-css-extract-plugin
CopyWebpackPlugin - copy the static assets
[Environments]
EnvironmentPlugin - add variables based on environment
[Optimisation]
TerserWebpackPlugin - adding minified js file in final bundle
[Minification]
CssMinimizerWebpackPlugin - css minification
[BundleAnalysis]
webpack-bundle-analyzer - for large apps with n num of dependencies, to avoid duplicate / unused dependencies and optimized the bundle.
plugins are available from npm.

HTML plugins

It will generate a new index.html file into the dist folder and it has index.bundle.js and all the other js files into it within script tag.
Since we already have index.html file in our source folder then we have to copy our contents into newly generated index.html file residing inside dist folder. to use it we will use html-webpack-plugin

Secondly, we have dist/index.html that has dependency on file index.bundle.js. So as to include index.bundle.js in dist/index.html we use chunks: ["index"] and we will remove the script import index.bundle.js from original index.html file because dist/index.html is already doing it for us.

plugins:[ // can add multiple plugins
  new HtmlPlugin({
    template: "./index.html", // to copy index.html contents into new one
    chunks: ["index"]
  })
],

CSS loader V/S mini-css-extract-plugin
CSS loader embeds html and css into same file and keeps the common file.
whereas mini-css-extract-plugin keeps the css file separated and helps in caching of any of them when there is any change in other file.
In CSS loader the whole file refreshes and flicker is seen if any changes is seen in html or css file.
Plugin include into plugins array
new MiniCSSExtractPlugin()
But our css is not optimized using this

Webpack Dev Server & Hot Module Replacement

To automatically get update of the code when we make changes into our files then we have to use dev server.
I all the above implemetatin we used to run it from the local files but not deployed to local server. so we use webpack-dev-server
npm install webpack-dev-server --save-dev
add "start": "webpack serve --open --mode development" in package.json then
npm start
this will start the server in the browser. with localhost:8080 port.

Without using "--mode development" it is showing below error.
Refused to connect to 'http://localhost:8080/.well-known/appspecific/com.chrome.devtools.json' because it violates the following Content Security Policy directive: "default-src 'none'". Note that 'connect-src' was not explicitly set, so 'default-src' is used as a fallback.
Even if we delete the dist folder then it will work fine also.

to run on specific port update webpack.config.js

devServer: {
    port: 3000,
    hot: true,          // ✅ enables Hot Module Replacement
    liveReload: true,   // ✅ fallback if HMR is not supported
}

Webpack Development and Production Mode

"build": "webpack --mode production",
"dev": "webpack --mode development"

In production mode we have minified files that will be confined to a single line. This is possible due to webpack internally uses treasurer pluggin that optimises code in production build.

We can separate dev and prod build config.js files.

Combined prod and dev webpack builds

"prod": "webpack --mode production",
"dev": "webpack --mode development",
"start": "webpack serve --open --mode development"

Separated development and production mode webpack config files.

Must use mode: "development", and mode: "production" in module.exports
webpack.dev.config.js
webpack.prod.config.js // dev code not required here

Optimize Your CSS: Minifying with the CSS Minimizer Webpack Plugin

css-minimizer-webpack-plugin

  • This plugin uses cssnano to optimize and minify your CSS.
  • Just like optimize-css-assets-webpack-plugin but more accurate with source-maps and assets using query string, allows caching and works in parallel mode.
  • This only optimizes our build in production mode.
  • This plugin will stops optimization of JS files and we require treasuer plugin or use spread operator to retain JS file optimization.
  • Automatically does removal of duplicate code for us.
  • It merges/ combine the css data (not file) into single one. e.g. if we have duplicate css data
    optimization: {
      minimizer: [
        `...`,
        new CSSMinimizerPlugin()
      ]
    }

Remove Duplicate CSS with CSS Minimizer Webpack Plugin and CSSNano

How is it doing behind the scenes.

  • Automatically does removal of duplicate code for us.
  • It merges/ combine the css data (not file) into single one. e.g. we have duplicate css data

Eliminate Unused CSS: Optimize Your Build with PurgeCSS

use PurgeCSS to remove unused css from the code.
npm install purgecss-webpack-plugin glob --save-dev
glob is also required
glob - scans all folder collect css file and pass it to purgecss to remove unused css.
Better to pass the whole folder path e.g. src folder

const PATH = {
    src: path.join(__dirname, ./src) // We should use ./src in directory name
}

plugins:[ // can add multiple plugins
      new PurgeCSSPlugin({
            // we need to pass the path that needs to be scanned by purgeCss
            // pass that path in diff way with the help of glob library
            paths: glob.sync(`${PATH.src}/**/*`, {nodir: true}),
            only: ["explore"], // do only here
            safelist: ["unused-css"] // Avoid removing   
            // glob.sync will asynchronously checks the PATH 
            // ** means all the directory
            // * means all the file
      }),
]

Tree-Shaking with Webpack 5: Removed Unused Code

Removing the unused code during the production build is automatically done by webpack.

  1. It uses static analysis to identify the dead code and its done through dependency graph.
  2. It works on analysing the import and export for ES6 modules.
  3. Enabled by default by webpack for production.
  4. Try to minimize your dependencies.
  5. Use dynamic imports whenever possible.
  6. Works only in production mode.

Understanding Webpack's ContentHash: Optimizing Long-Term Caching

Adding unique hash to our file name and how does it help in long term caching.
We add a hash to our file name and hash value gets updated whenever there is any change into the file contents.
This is helpful in performance.
output file:
filename: "[name].[contenthash].js"

specific file: e.g index.html or Dependent files
Don't do content hashing of these files. Make sure it is not used anywhere in the project (AVOID IT)

Webpack Code Splitting & Analyzing Bundle

webpack-bundle-analyzer
run npm install webpack-bundle-analyzer --save-dev
then run app in dev mode.

let us use 2 dependencies for common dependency example.
Lodash and dayjs

then it will run on port http://127.0.0.1:8888/ showing the dependencies it uses.

Code splitting can be achieved if u have same dependency shared across multiple entries or multiple pages.

  optimization: {
    splitChunks: {
      chunks: "all"
    }
  }

const _ = require('lodash');
// Example: Splitting an array into chunks
const array = [1, 2, 3, 4, 5, 6];
const chunkedArray = _.chunk(array, 2);
console.log(chunkedArray);

import dayjs from 'dayjs';
const date = dayjs().format('YYYY-MM-DD HH:mm:ss');
console.log(date);

in dev mode it creates a file "vendors-node_modules_dayjs_dayjs_min_js-node_modules_lodash_lodash_js.bundle.js" which contains all the common dependency code.

when used in prod mode,
it creates a new file "519.5ba2040f19829bf9ea22.js" which contains all the common dependency code.

Webpack 5 Dynamic Imports and Lazy Loading

Dynamic import and lazy loading is used when we always don't need a required code or dependency and we import it when it is required.
e.g.--
Dynamic import using import("lodash")

import("lodash").then(({default: _}) => { // dynamic import
const array = [1, 2, 3, 4, 5, 6];
const chunkedArray = _.chunk(array, 2);
console.log(chunkedArray);

const date = dayjs().format('YYYY-MM-DD HH:mm:ss');
console.log(date);
})

Webpack Source Maps Explained

  • Source map is one of the most powerful feature of webpack that helps develop debug their code more efficiently.
  • Production build is optimized, compressed and minified.
  • They map your minified code back to original source code.

WHY WE NEED SOURCE MAP

  1. Debugging over production code.
  2. Error Reporting
  3. Development Efficiency

HOW SOURCE MAP WORKS

  1. They map your minified code back to original source code.
  2. Integration with browser developer tool

HOW TO ENABLE

  1. Development - By default enabled.
  2. Production - We need to set devtool property with required option

WHEN TO USE IT

  1. Development - Keep it always enabled.
  2. Production - Based on requirement
    Disadv. It makes our bundle size large and based on our requirements we should use it wisely on production with best practises.

PROS

  1. Improve debugging
  2. Error Tracking
  3. Code readability
    CONS
  4. Performance overhead
  5. Security risk - anyone can check our code.
  6. Bundle size

use Development devtool: "eval",// false it is enabled by default in dev mode
it has "eval" value embed in the encrypted source code.

in Production => devtool: "source-map",
it generates a new file in prod mode i.e. with .map extention

use "eval" when using in dev mode and "source-map" in prod mode.

It will be good if we don't deploy it live instead use it for testing purpose for error detection.

Deploy Static Websites and Apps with Surge

Webpack creates build with static files only i.e. html, js , css, images etc. and no backend is involved into it.

Javascript

  Let and const scope of let b in outside and inside of block value of a and b before initialization Multiple blocks Closures var and let in...