Skip to main content

Command Palette

Search for a command to run...

webpack basics

Published
•5 min read•View as Markdown

In this tutorial You will learn what is webpack? How to bundle js, ts, jsx, tsx, html, css files in webpack?.

And at last we will use babel transcompiler to compile out jsx and use it in browser.

webpack

  • Webpack is bundler that bundles multiple js files in a single js file.

  • Webpack bundles only .js files, to bundle other files, we need something called Loaders. There are many loaders available for each specific files, you check the docs

  • we will discuss some of the loaders for html, css, ts, tsx/jsx

  • add some dependencies in package.json to use webpack.

    "devDependencies": {
      "webpack": "^5.104.1",
      "webpack-cli": "^6.0.1",
      "html-webpack-plugin": "^5.6.5",
    
      "css-loader": "^7.1.2",
      "style-loader": "^4.0.0",
      "mini-css-extract-plugin": "^2.9.4",
    
      "ts-loader": "^9.5.4",
      "typescript": "^5.9.3",
    
      "@babel/core": "^7.28.5",
      "@babel/preset-env": "^7.28.5",
      "@babel/preset-react": "^7.28.5",
      "babel-loader": "^10.0.0"
    },
    "dependencies": {
      "react": "^19.2.3",
      "react-dom": "^19.2.3"
    }
    
  • webpack has default configurations which can be run via webpack --mode production. Output is thrown in ./dist by default.

  • add these scripts in package.json script field:

    "scripts": {
      "bundle:prod": "webpack --mode production --watch",
      "bundle:dev": "webpack --mode development --watch",
    }
    
    • production mode optimizes the code and --watch flag watches the change.
  • By Default webpack bundles code of ./src/index.js to ./dist/main.js or ./dist/bundle.js. webpack default configs can be overridden by webpack.config.js file in root.

    // webpack.config.js
    // get a nice suggestions in .js file ↓
    /** @type { import("webpack").Configuration } */
    export default {
    // entry: "", // change if needed
    // output:"", // change if needed
    module: {
      rules: [
        // loaders goes here
      ],
    },
    plugins: [
      // plugins goes here
    ],
    };
    

loaders

  • webpack by default build only .js files. Other files like css, ts, html, needs their specific loader and plugins.

html

  • webpack need a html-webpack-plugin. It generates a html file from the bundled javascript.
import HtmlWebpackPlugin from "html-webpack-plugin";

export default {
  plugins: [new HtmlWebpackPlugin()],
};

Now you can open dist/index.html file in browser (* use live-server vsc extension to auto update on file change).

  • if you have your own index.html, give its path to the htmlWebpackPlugin.w
import HtmlWebpackPlugin from "html-webpack-plugin";

export default {
  plugins: [new HtmlWebpackPlugin({ temeplate: "./src/index.html" })],
};

css

  • css is loaded by css-loader and style-loader (pnpm add css-loader style-loader -D) css-loader only generate styles from .css files whereas style-loader injects generated styles to .html .

      // webpack.config.js
      /** @type {import("webpack").Configuration} */
      export default {
        module: {
          rules: [
            {
              test: /\.css$/,
              use: ["style-loader", "css-loader"],
              // order should be like this ← ← ← ←
              // css-loader will pass output to style-loader
            },
          ],
        },
      };
    
    • You will notice the inline styles generated by style-loader.

    • To extract css in a seperate main.css file, use mini-css-extract-plugin

          import MCEP from "mini-css-extract-plugin";
          rules: [
            {
              test: /\.css$/,
              use: [MCEP.loader, "css-loader"], // pass styles output to MCEP
            },
          ],
      
          plugins: [new MCEP()] // register plugin and use its loader in css rules
      

      By default MCEP adds a main.css file in dist and link it in the index.html.

  • Now run dist/index.html in browser and you can see the js code being executed on browser.

ts

  • ts files are loaded by ts-loader

  • pnpm add typescript ts-loader -D

  • add a very basic tsconfig.json

      {
        "compilerOptions": {
          "rootDir": "src",
          "outDir": "dist"
        },
        "include": ["src/**/*"]
      }
    
  • use ts-loader for ts files in webpack.config.js

      rules: [
        {
          test: /\.ts$/,
          use: ["ts-loader"],
        },
      ];
    
  • you are ready to import ts files in js. and webpack will bundle it via ts-loader import { Web } from "./web.ts"

  • But this is a very odd way to import files, Usually we don't use extensions to import import { Web } from "./web". This throws error becoz webpack does not know if it is a ts or css or any other file.

  • Webpack tries to resolve ./web.js which is not available

  • Solution: Tell webpack if a file extension is not given, automatically resolve it to .ts

//webpack.config.js
export default {
  module: { rules: [ ... ], },
  resolve: {
    extensions: [".ts", ".tsx", ".js", ".jsx"], // you can add multiple extension resolvers
  },
  plugins: [...],
};
  • import { Web } from "./web" resolves to → ./web.js, ./web.jsx, ./web.ts, ./web.tsx

jsx or React

  • like other loaders we need a tsx or jsx loader like babel-loader.

  • Babel is a transcompiler which compiles js source code to another js code which makes it compatible with other older browser versions.

  • use @babel/preset-react to convert jsx → js

  • pnpm add @babel/core @babel/preset-react babel-loader -D

      // add to rules
      {
        test: /\.jsx$/,
        use:{
          loader: "babel-loader",
          options: {
            presets: ["@babel/preset-react"], // to compile .jsx | .tsx files it need @babel/preset-react
          },
        },
      }
    
  • Now webpack will successfully compile jsx into js and dist/index.html can be load on browser

  • But still you will see nothing on browser or may be in browser console you will see error: React not found.

    • @babel/preset-react converts jsx to js in following way:
    // src/index.jsx
    function App() {
      return (
        <div className="app">
          <div className="message">Welcome to App</div>
          <div>Hello World</div>
        </div>
      );
    }
    // ↓ converts to somewhat like
    // dist/bundle.js
    React.createElement(
      "div",
      { className: "app" },
      React.createElement("div", { className: "message" }, "Welcome to App"),
      React.createElement("div", null, "Hello World")
    );

    // *you can confirm by searching the funcition Name in dist/bundle.js
  • when this React.createElement code reaches browser, React is not able to resolve, as there are no node_modules in browser.

  • Hence even if webpack bundled code correctly, but in runtime browser is unable to resolve the code.

  • Solution: make runtime automatic in babel react preset → It imports react package in the bundled js instead of relying on node_modules.

    // webpack.config.js
    presets: [
      //"@babel/preset-react" → invalid
      ["@babel/preset-react", { runtime: "automatic" }],
    ];
  • Now check the dist/bundle.js you will not find any direct React function call.

  • This solves the error and make react code ready to run on browser as usual.