The Spiritual Shift from Gulp to Webpack

January 2, 2019 9 min read

Andrew Welch’s landmark webpack treatise covers just about everything you’d need to get working with an organized, comprehensive webpack-based workflow. I know this because I’d recently made the transition from Gulp to webpack with only a fraction of what’s covered in his post. It took me a long time. If you’re looking for an up-close view of the machinery, Andrew’s article will make the most of your five minutes.

If you’re still not sure that webpack makes any sense, or how it can be okay to have 66 dependencies for a demonstration, join me here for a moment.

I’d started pawing at webpack a few times only to get confused or frustrated at the lack of a Gulp-like simplicity.

In hindsight, simplicity was the wrong thing to look for because webpack is just more complicated.

Making produtive use of it demanded that I rethink my approach to front end assets and how they come together. It took me a while to hammer out my humble workflow, and I’d like to share the critical shift in orientation that finally made all the technical hurdles worth the trouble.

Webpack is for Modern JavaScript

As much as I don’t want to admit it, Peter Jang’s Modern JavaScript Explained for Dinosaurs was the first article that put things into context for me. Webpack doesn’t just do stuff to files. It understands packages and dependencies and web performance. That’s massively important and distinctly different from Gulp.

Imagine a project that relies on jQuery, some plugins and scripts, and a Gulp workflow. For that project, webpack will feel like overkill. It probably is overkill. The retooling and complication may not be worth it for every project.

Another project may use ES6 and Babel, Vue/React, PostCSS, and incorporate Critical CSS and polyfills and asynchronous JavaScript loading. If you’ve built such a project without using webpack, odds are you’ve found yourself in a bit of a kerfuffle. These are great tools, and yet getting everything neatly packaged and cache-broken for production gets increasingly tedious. Once your expanding front end toolset starts to feel like a herd of cats, that’s when webpack’s majesty reveals itself. Whether you’ve experienced that pain or not, the first step is to understand how webpack is designed to solve problems differently than Gulp.

A Conveyor Belt and a Production Manager

Gulp and webpack are different animals.

Using Gulp is like having a conveyor belt where you can tinker with each of the stations. You drop stuff in at different points, it all moves down the line, and it’s dumped out somewhere.

Using webpack is more like hiring a production manager to listen to all your needs and wrangle all the stuff efficiently. Instead of adjusting conveyor belt stations, you tell the production manager where to find things and what you’d like to end up with. You spend most of your time going into great detail clarifying your instructions, and the manager gets it done.

This manager only understands JavaScript so you’ll have help convey what CSS and static files are, but the manager understands packages, dependencies, inputs and outputs. This is a big deal, because it’s not just acting on files but working more knowingly with code. The manager also has a network of friends (plugins) that can add expertise to the production department. If you’re all working well together, stuff will end up where you need it and how you need it, for development or production. It’s very fast, too.


Webpack doesn’t just want to compile your things and dump them out, it wants to help you solve production problems. "Help me help you," it says. Here are my tips for working with webpack:

Don’t skim when you’re reading about entry points.

The webpack magic starts and ends with entry points, so it’s important to understand what they are and how you can use them. The concept shouldn’t be too unfamiliar: entry points are the built assets you’ll reference in your markup so the browser can work with your bundles.

Your current setup might have a global app.js that handles site-wide navigation and search functionality, for example, and then forms.js that you use only on pages with forms. Each could easily be an entry point in your webpack setup.

Where things can get more interesting is that a single entry point can load code asynchronously, dynamically split code, and usher in all kinds of fancy that doesn’t have an equivalent with your current setup.

But they don’t have to be complicated. The entire contents of an entry point might look like this:

import '../css/forms.css';

That’s an imaginary /src/js/forms.js file containing only a CSS import. As I mentioned above, webpack understands JavaScript—but a common practice is to lump in CSS and have webpack split out and process JS and CSS separately. After the webpack magic is complete, it’ll have built /dist/css/forms.bundle.css which I can include on the front end. In this case I’d also have a /dist/js/forms.bundle.js I’d ignore.

I can, however, start including other JS code or writing JavaScript directly in that entry point and drop the resulting /dist/js/forms.bundle.js in my site’s markup.

Know what an entry point is and how you intend to use one or several of them in your project.

Don’t bring files and expect to leave with some other files.

Bring packages and code and expect to leave with bundles.

If you’re used to downloading JavaScript, putting it in a publicly-accessible folder, and adding script tags to your markup before relying on that code, your life is going to get way easier and your projects are going to get much tidier.

You might start whistling while you work.

For example, let’s say you’re using Prism to add syntax highlighting to your code blocks.

Previously, you might have visited the download page to customize and download a build with whatever features you’d want. You’d plop them in your web folder and add the JS and CSS to your markup:

<script src="prism.js"></script>
<link rel="stylesheet" href="prism.css">

You could have put them in a project source folder and had gulp minify and combine them as well, but ultimately you were putting the result in the web root one way or another. You would then have to manually grab new files for changes, whether it’s to Prism’s core code, a change of color theme, or different language support.

Including Prism’s JavaScript alone will have it get to work on your page’s code blocks, but you can also add a data-manual property to your script tag to prevent that and configure it via API.

<script src="prism.js" data-manual></script>

You’d then write some code telling Prism what to do, putting it in <script> tags on your page or in another file (like app.js) that you include as well. This is the same approach as including a jQuery script tag, some jQuery plugin script tags, and then adding your own JavaScript file or script block to use jQuery and its plugins to make stuff happen.

Now let’s look at this Prism example with a webpack setup.

By virtue of using webpack, we’ve probably already included bundle references somewhere in our markup:

<script src="app.bundle.js"></script>
<link rel="stylesheet" href="app.bundle.css">

The app. filename is deliberate, because this bundle is actually a collection of different things and not just our Prism assets; each file is the result of webpack’s bundling process. That process likely includes combining and minifying files (among other things), resulting in app.bundle.js and app.bundle.css. You can choose whatever filenames you’d like, of course.

Webpack knows what to do with npm packages, which you probably already know are a vast expanse of JavaScript repositories you can neatly require with npm or yarn at the command line.

That’s exactly what we’ll in the webpack equivalent of our example:

npm install prismjs

npm now downloads Prism for you into your project’s node_modules directory. Unless you’ve done something horribly wrong, node_modules shouldn’t be web-accessible.

First we’ll want to load Prism into some JavaScript in our project:

import Prism from 'prismjs';

Nothing happens with this alone, so we have to add one line that tells Prism to highlight all the code blocks it finds:

import Prism from 'prismjs';


That’s it! As long as this JavaScript is included in a webpack entry point, it’ll get bundled with everything else. No need to move anything else into your web root or update your markup. You also get a few more perks that come with package management:

  1. When you npm install, the package and its version are added to your package.json file. This will signal to other developers that your project uses Prism, and the package manager will look out for compatibility issues and make sure any project code can share the same version of Prism to keep things efficient.
  2. Your repository is cleaner since dependencies are downloaded separately. (Commit your bundles, don’t commit your node_modules.) This can be helpful for licensing, too.

Webpack will champion your dependencies and performance needs, so try to shift your thinking toward what code you need and where it should have an impact rather than what files you need and how you’ll manage them in your webroot and your markup.

Be patient.

I found many helpful articles on my quest that had totally incompatible approaches to solve similar problems. It’s complicated, there’s a whole lot you can do, and it’ll feel fantastic when all the pieces are working for you and speeding up your development process.

I’m far from an expert on the subject, but my prior confusion was genuine and long-lasting. Despite the significant effort to use webpack in my projects, my code is cleaner and my build process is much smoother and I’d probably have tried to make the move sooner if I’d known how much it would improve my development efforts in general.

I hope this is helpful! Please post a comment or send me an email if you have questions, comments, or corrections.

Matt Stein’s face

by Matt Stein

Full stack tinkerer, sporadic blogger and Craft CMS fan occasionally found on the podcast.

Updated 6/16/19 at 7:26pm