How to Setup a Javascript Project with Webpack

Have you ever wished you could stop manually adding all your scripts as tags in your html file?

Category Fundamentals | Tags: Webpack

Published: 29 April 2020


Out of the box, Webpack is going to allow you to just have one script tag in your html file. The way it does this is by creating a dependency graph and then bundling all your code together. The dependency graph for the sample ToDo App we're going to make looks like this.

Alt Text

Webpack crawls over your code, and finds all your import and export statements and then pulls together all the code that you are actually using and bundles it all together in one file that you just include in your html file.


Before we do anything you're going to have to lay some groundwork. Get in a new folder and set up a file directory that looks like the below example. You need build and src to be folders that contain the pictured files.
Alt Text

Now in the root directory let's set some things up. First run npm init -y.
This will set up a package.json file with all the default settings.

Now we need to install a few modules. --save-dev just denotes that they are just going to be saved as development dependencies and won't overly bloat your final build. So run npm install --save-dev webpack webpack-cli.

So let's go ahead and make an app that displays a very simple to do list. First lets set up your index.html.

<!DOCTYPE html>
<html lang="en">

    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>To Do List Webpack Demo</title>
    </head>

    <body>
        <div id="app"></div>
        <script src="app.js"></script>
    </body>

</html>

Now before we actually add any Javascript code let's take a look at what our app is going to do. So here's all of its functionality.

function toDoList(list) {
    const newElement = document.createElement('ul');
    newElement.innerHTML = "To Do List";
    list.forEach(item => { newElement.append(toDoListEntry(item)); });
    return newElement;
}

function toDoListEntry(item) {
    const newElement = document.createElement('ul');
    newElement.innerHTML = item;
    return newElement;
}

const toDos = ['Go to store', 'Clean house', 'Feed dogs'];

document.getElementById('app').appendChild(toDoList(toDos));

Pretty simple, right?

No reason we can't have all that in one file, but let's pretend it's way more complex and we want to split both of those functions out into their own files. So let's add a couple more files to our file tree. It should now look like this.
Alt Text

Now let's go about hooking them all up with imports and exports! Let's take a look at the toDoListEntry.js first.

function toDoListEntry(item) {
    const newElement = document.createElement('ul');
    newElement.innerHTML = item;
    return newElement;
}

export default toDoListEntry;

We just add an export statement to the bottom to export the function we want other files to be able to see and default will just make it quicker to do that since there's only one thing we want from this file.

Now let's do the same thing to toDoList.js

import toDoListEntry from "./toDoListEntry.js"function toDoList(list) {
    const newElement = document.createElement('ul');
    newElement.innerHTML = "To Do List";
    list.forEach(item => {
        newElement.append(toDoListEntry(item));
    });
    return newElement;
}

export default toDoList;

We've added an import statement at the top of the file to get access to the default export from toDoListEntry.js. Then we can just call it in our forEach loop just like before.

And then we just export toDoList.

Just one more file to set up now. Let's take a look at index.js.

import toDoList from "./toDoList.js";
const toDos = ['Go to store', 'Clean house', 'Feed dogs'];
document.getElementById('app').appendChild(toDoList(toDos));

All we do is import toDoList from toDoList.js and call it just like before.

Real quick, we need to create a webpack.config.js. And start it off like this

module.exports = {
    entry: './src/index.js',
    output: {
        filename: 'app.js',
        path: __dirname + 'build',
        publicPath: '/'
    }
}

The entry point is the file that it's going to look at first to build it's dependency graph from.

The output is where the compiled code is going to go. The filename is the name of the file and the path is what folder you want the file to be in.

So now if you run npx webpack --config ./webpack.config.js it will compile the code for you and you will have an app.js waiting for you in your build folder. But typing that out every time you make a change to your code will get old fast. So let's add a new script to your package.json file that takes care of it for us.

"start""webpack --config ./webpack.config.js --watch"

The --config just tells it where to look for your config file, and --watch just tells it to keep updating if you change any files. And now you can just run npm start and your app.js file will automatically update!

Let's make one last quality of life update to our webpack.config.js file. We're going to put it in development mode, so that we can actually read any errors and actually debug our code.

module.exports = {
    entry: './src/index.js',
    output: {
        filename: 'app.js',
        path: __dirname + '/build',
    },
    devtool: 'eval-source-map',
    mode: 'development'
}

Putting it in development mode keeps the code from being minified and setting the devtool to 'eval-source-map' traces any errors we might have so that they're easy to find.

So as you can see, even though this project is so small it probably didn't need webpack, it still demonstrates the usefulness of the tool. As your projects get larger and larger, webpack will help you keep your code organized and looking sleek!