My Webpack Setup for a Vanilla JavaScript Excel Clone


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:
Package | Why I Use It |
webpack | The core bundler |
webpack-cli | Command line interface |
webpack-dev-server | Local dev server with hot reload |
html-webpack-plugin | Automatically injects scripts/styles into HTML |
clean-webpack-plugin | Cleans /dist before each build |
copy-webpack-plugin | Copies static assets (like favicon) |
mini-css-extract-plugin | Extracts CSS from JS |
sass , sass-loader , css-loader | Enables SCSS support |
babel-loader , @babel/preset-env | Transpiles modern JS for compatibility |
cross-env | Handles 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!
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.