My Webpack Setup for a Vanilla JavaScript Excel Clone

OleksiiOleksii
3 min read

Hey friends!

If you remember, in my previous post, I shared that I decided to build a mini Excel clone using vanilla JavaScript – without any frameworks. This time, I want to show you how I set up Webpack manually, why I chose specific tools, and what challenges I encountered along the way.

No Vue. No React. No Create-React-App.

This is pure JS, DOM, modules, and hands-on build setup — because I want to truly understand how everything works under the hood.

What I Wanted from My Setup

Before I started writing any logic, I knew I wanted:

  • SCSS support with automatic bundling

  • Modern JavaScript (ES6+) via Babel

  • Live reloading while developing

  • A proper production build with hashed assets

  • Clean, minimal config with full control

Tools I Chose and Why

Here are the key dependencies and why I picked them:

PackageWhy I Use It
webpackThe core bundler
webpack-cliCommand line interface
webpack-dev-serverLocal dev server with hot reload
html-webpack-pluginAutomatically injects scripts/styles into HTML
clean-webpack-pluginCleans /dist before each build
copy-webpack-pluginCopies static assets (like favicon)
mini-css-extract-pluginExtracts CSS from JS
sass, sass-loader, css-loaderEnables SCSS support
babel-loader, @babel/preset-envTranspiles modern JS for compatibility
cross-envHandles environment variables across OSes

webpack.config.js

const path = require("path");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const HTMLWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const CopyPlugin = require("copy-webpack-plugin");

const isProd = process.env.NODE_ENV === "production";
const isDev = !isProd;

module.exports = {
  context: path.resolve(__dirname, "src"),
  mode: "development",
  entry: "./index.js",
  output: {
    filename: "bundle.[hash].js",
    path: path.resolve(__dirname, "dist"),
  },
  resolve: {
    extensions: [".js"],
    alias: {
      "@": path.resolve(__dirname, "src"),
      "@core": path.resolve(__dirname, "src/core"),
    },
  },
  devServer: {
    port: 4200,
    hot: isDev
  },
  plugins: [
    new CleanWebpackPlugin(),
    new HTMLWebpackPlugin({
      template: "index.html",
    }),
    new CopyPlugin({
      patterns: [{ from: "favicon.ico", to: "favicon.ico" }],
    }),
    new MiniCssExtractPlugin({
      filename: "bundle.[hash].css",
    }),
  ],
  module: {
    rules: [
      {
        test: /\.s[ac]ss$/i,
        use: [MiniCssExtractPlugin.loader, "css-loader", "sass-loader"],
      },
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: "babel-loader",
          options: {
            presets: ["@babel/preset-env"],
          },
        },
      },
    ],
  },
};

Final Thoughts

This setup gives me a clean, flexible development environment for working with vanilla JS and SCSS. I now have full control over the bundling process, and I learned a lot about how Webpack really works under the hood.

If you’re learning frontend or want to practice building without frameworks — try this approach. It’s fun, challenging, and gives you a deeper understanding of the frontend toolchain.

Let me know in the comments if you’d like to see how I organize components and state in vanilla JS — or maybe how I structure the spreadsheet logic next!

0
Subscribe to my newsletter

Read articles from Oleksii directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Oleksii
Oleksii

Frontend developer focused on Vue 3, Nuxt, TypeScript & Tailwind. Writing clean, scalable code and sharing what I learn along the way.