Creating React SPA Boilerplate with Webpack

ranggapr

Rangga Putra

Posted on January 15, 2023

Creating React SPA Boilerplate with Webpack

First, initiate the project by running

npm init
Enter fullscreen mode Exit fullscreen mode

After it's done, let's install the packages we need to run a basic React Single Page Application.

npm i react react-dom react-router-dom
Enter fullscreen mode Exit fullscreen mode

For us to be able to use modern Javascript we need babel to compile our codes.

npm i -D @babel/core @babel/preset-env @babel/preset-react babel-loader
Enter fullscreen mode Exit fullscreen mode

create a .babelrc file to place our babel configuration, for now, only set it to use the preset-env and preset-react

// .babelrc

{
  "presets": ["@babel/preset-env", "@babel/preset-react"]
}
Enter fullscreen mode Exit fullscreen mode

Now let's bring webpack into our project

npm i -D webpack webpack-cli webpack-dev-server webpack-merge html-webpack-plugin
Enter fullscreen mode Exit fullscreen mode

we will use different webpack configurations for the development and production server, so will split our webpack configuration into several files, so later we need to merge it with webpack-merge.

The html-webpack-plugin will generate an HTML5 file for us that includes all your webpack bundles. The webpack-dev-server is required to serve our project in development, and we will use the webpack-cli to build and serve our project.

Now we have to setup the webpack configurations and the app's entry point. First, we create the common webpack configuration in a file named webpack.common.js in there we will set the app's entry point, how files are resolved, and bundles output and we will also configure webpack to handle asset files like images and font.

// webpack.common.js

const path = require("path");

module.exports = {
  entry: {
    app: "./src/index.js",
  },
  resolve: {
    extensions: ["*", ".js", ".jsx"],
  },
  output: {
    filename: "[name]-[fullhash].js",
    path: path.resolve(__dirname, "dist"),
    clean: true,
  },
  optimization: { runtimeChunk: true },
  module: {
    rules: [
      {
        test: /\.(js|jsx)$/,
        exclude: /node_modules/,
        loader: "babel-loader",
      },
      {
        test: /\.(svg|ico|png|jpg|jpeg|gif|webp)$/,
        use: {
          loader: "file-loader",
          options: {
            name: "[name]-[fullhash].[ext]",
            outputPath: "assets/img",
          },
        },
      },
      {
        test: /\.(otf|eot|ttf|woff2|woff)$/,
        use: {
          loader: "file-loader",
          options: {
            name: "[name]-[fullhash].[ext]",
            outputPath: "assets/font",
          },
        },
      },
    ],
  },
};
Enter fullscreen mode Exit fullscreen mode

Next, let's create a configuration for the development, and name the file webpack.dev.js we will merge the common configration with this development-only configuration in which we set the dev server, and type of source-map and we will also configure webpack to handle CSS files and HTML templates.

// webpack.dev.js

const path = require("path");
const { merge } = require("webpack-merge");
const common = require("./webpack.common.js");
const HtmlWebpackPlugin = require("html-webpack-plugin");

module.exports = merge(common, {
  mode: "development",
  devtool: "eval-source-map",
  module: {
    rules: [
      {
        test: /\.css$/,
        use: ["style-loader", "css-loader"],
      },
    ],
  },
  devServer: {
    static: {
      directory: path.join(__dirname, "dist"),
    },
    liveReload: true,
    open: true,
    port: 8080,
    historyApiFallback: true,
    compress: true,
  },
  optimization: {
    minimize: false,
  },
  plugins: [
    new HtmlWebpackPlugin({
      title: "react app",
      template: "./src/index.html",
    }),
  ],
});
Enter fullscreen mode Exit fullscreen mode

Finally, we create a configuration for the production. for the production build, we want to minimize the bundles so we add additional plugins to optimize our file, for CSS we use css-minimizer-webpack-plugin to minify the CSS using nanocss under the hood and mini-css-extract-plugin to extract our CSS into separated bundle file. so let's install both of them.

npm i -D css-minimizer-webpack-plugin mini-css-extract-plugin
Enter fullscreen mode Exit fullscreen mode

For HTML optimization, we will use the HtmlWebpackPlugin to minify our document, while for Javascript we can use terser which is included in webpack 5 and enabled by default.

// webpack.prod.js

const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const common = require("./webpack.common.js");
const { merge } = require("webpack-merge");

module.exports = merge(common, {
  mode: "production",
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [MiniCssExtractPlugin.loader, "css-loader"],
      },
    ],
  },
  optimization: {
    minimize: true,
    minimizer: [
      `...`,
      new CssMinimizerPlugin(),
      new HtmlWebpackPlugin({
        template: "./src/index.html",
        minify: {
          collapseWhitespace: true,
          removeComments: true,
          removeRedundantAttributes: true,
        },
      }),
    ],
  },
  plugins: [new MiniCssExtractPlugin()],
});

Enter fullscreen mode Exit fullscreen mode

For the React app's entry point, first let's create a folder called src inside the folder, create an HTML5 document to attach our React app into

// src/index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width,initial-scale=1.0" />
    <title><%= htmlWebpackPlugin.options.title %></title>
  </head>
  <body>
    <noscript>
      <strong
        >We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work
        properly without JavaScript enabled. Please enable it to
        continue.</strong
      >
    </noscript>
    <div id="app"></div>
  </body>
</html>
Enter fullscreen mode Exit fullscreen mode

Let's also create a React component with a little hello world in it

// src/App.jsx

import React from "react";

const App = () => {
  return <h1>Hello World</h1>;
};

export default App;

Enter fullscreen mode Exit fullscreen mode

Finally, create a Javascript to render our main React app and setup react-router routes

// src/index.js

import React from "react";
import { createRoot } from "react-dom/client";
import { createBrowserRouter, RouterProvider } from "react-router-dom";
import App from "./App";

const router = createBrowserRouter([
  {
    path: "/",
    element: <App />,
  },
]);

createRoot(document.getElementById("app")).render(
  <React.StrictMode>
    <RouterProvider router={router} />
  </React.StrictMode>
);
Enter fullscreen mode Exit fullscreen mode

Let's also add scripts to run and build our project to the package.json

  "scripts": {
    "dev": "webpack serve --config webpack.dev.js",
    "build": "webpack --config webpack.prod.js"
  },
Enter fullscreen mode Exit fullscreen mode

Now try running npm run dev command and it should work and launch a development server

💖 💪 🙅 🚩
ranggapr
Rangga Putra

Posted on January 15, 2023

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related