When starting a new JavaScript project, one of the first things you’ll do is set up a build system. But with so many options, deciding on tools often gets in the way of building the app itself.
Imagine if there was a simple rule you could follow to choose which build tools to use – wouldn’t it be great being able to just get stuck into writing your app? Actually, after spending five years writing apps with automatic build systems, I’ve come upon just such a guide. I know what to use and where to use it — and after reading this article, you will too!
Want a boilerplate for your Single Page App with my recommended build system? Check out the Unicorn Standard React/Redux Boilerplate!
The Short Answer
It is easy to decide which tools you need:
- Small projects can get away with just an ES6 compiler
- Single Page Apps need a module bundler too
- Once your app is in production, use a task runner to automate anything else
And here are the packages which fulfil these requirements:
- For compiling and polyfilling ES6, use Babel
- For bundling your JavaScript files and dependencies into static assets, use Webpack
- If your have other tasks like renaming files to avoid caching or publishing to the web, automate them with Gulp
But why are these tools the right ones for the job?
The Details
To get the above answer, I looked at the strengths and weaknesses of a number of popular JavaScript build tools. Since compilers are the thing you’ll definitely need, let’s start with them.
Compilers
ES5 JavaScript is not the prettiest of languages. ES6 and ES7 improve things a lot, but that doesn’t help when the browsers are stuck in the past.
Luckily, a number of enterprising engineers have built compilers which turn new JavaScript into old JavaScript! Some even added extra features – like Microsoft’s TypeScript. But if we limit ourselves to standardised JavaScript, there is one tool which stands head and shoulders above the rest: @sebmck’s Babel.
Babel
If there is one tool you should learn to use after reading this guide, it is Babel.
Babel isn’t full of surprises – it just transforms ES6 to ES5 as you’d expect. But the wonderful thing about Babel is that it also allows you to add your own transforms – and the community has created a lot of them. In particular, Babel has transforms for ES7 features like async
/await
and decorators. It also transforms your React JSX.
But while Babel is great at transforming JavaScript, it can’t do anything else. In fact, it can’t even bundle together multiple files using ES6’s import
and export
statements. Which is why you’ll also need a module bundler.
Module bundlers
Most projects of any scale will have their code split between a number of files. And while you could just include each file with an individual <script>
tag, you’d then be in charge of manually ordering them based on dependencies. Computers can do that better than you can, and that is why you should use a tool to automatically bundle everything into a single file.
While the internet has spawned a bewildering array of module bundling tools, there are two which dominate the landscape: Browserify, and Webpack.
Browserify
Browserify is a tool for bundling Node packages for the browser. It also happens to work for browser-based apps pretending to be Node packages.
This Node-centric philosophy has a number of benefits; bundling an app with Browserify is simple, you can using built-in Node modules like path
, and you can use any code you’ve written for Node as is. The downside, of course, is that your Single Page App will generally need types of resources that Node apps won’t: CSS, images and fonts.
But this isn’t much of a downside, because a number of people have written plugins which teach Browserify to handle these resources too. There are plugins which let you transform ES6 to ES5, bundle CSS, split your bundle into multiple files, and even make you sandwiches. But given that these plugins are cajoling Browserify into doing something it wasn’t designed for, the configuration can get a little messy.
So while Browserify is a wonderful tool for bundling node modules for the browser, if you’re writing a Single Page App then you may prefer a tool designed for bundling anything.
Webpack
Webpack is a tool which takes JavaScript modules with dependencies and bundles them into files. It doesn’t give a hoot what those dependencies are, as long as they’re JavaScript modules.
As long as they’re JavaScript modules?! That means no CSS and no Images, right? Well, kind of – but not really – because Webpack has a wonderful way of turning anything into JavaScript modules: loaders.
Loaders are transformations that are applied to resource files. They can use anything for their input and output, not just JavaScript. They’re also chainable, which lets you do things like transforming your SCSS files to CSS before then transforming your CSS into a JavaScript module. And tada, Webpack can bundle a JavaScript module!
If Webpack has a downside, it is that a number of popular boilerplate projects include enormous configuration files which scare people off. But you won’t have that problem, because you can skip the boilerplates with my guide to configuring Webpack with Babel in 26 lines.
So now you know how to use module bundlers to produce a couple of static asset files. But what do you do with those files? That is where task runners come in.
Task runners
Task runners are tools for defining and running tasks. What do I mean by tasks? Simple: anything you might manually do from the command line.
Now it is important to understand that just because you can write a task for something, doesn’t mean that you should write that task. For example, it is possible to manually write tasks to bundle your modules. But assuming you delegate this to Webpack (as you should), you still need to start Webpack. And that is a task.
Other common tasks which can’t be handled by your module bundler include inserting <script>
tags for your generated asset files, and publishing your new build to the web.
After reading this, you may be thinking “but I already know how to run tasks with GNU Make”. And if you’ve been around for a while, there is absolutely nothing wrong with continuing to use what you know. But if you don’t have a preferred tool then it makes sense to learn one which other JavaScript developers use. And that means picking Grunt or Gulp.
Grunt
Grunt is a tool for running tasks which you’ve defined. And so it might surprise you that out of the box it basically can’t do anything.
The reason for this is that Grunt tasks are not defined with JavaScript code, but with configuration objects; they’re defined declaratively. And in order to keep the core Grunt package to a reasonable size, each of these configurations objects are handled by plugins – from watching for changes, to copying and concatenating files.
This does have its advantages; there are thousands of plugins, and you can often find one which does what you want without writing any code. The major issue is that if you do want to do something a little unusual, you can’t just write a task in plain ol’ JavaScript. Instead, you’ll have to write your own plugin. And then hope that nobody else ever has to look at your huge configuration file which is littered with obscure options for unmaintained plugins.
Gulp
Gulp, like Grunt, is a tool for defining and running tasks.
The major difference between Grunt and Gulp is that where Grunt defines tasks declaratively using configuration objects, Gulp defines tasks as JavaScript functions. And since Gulp knows how to handle functions which return streams and promises, you have a lot of flexibility in how you write your tasks.
Gulp, like Grunt, has a massive plugin library. But seeing Gulp tasks are just plain old functions, you’ll find you can also use a lot of vanilla node modules for your Gulp tasks too.
The biggest issue people have with Gulp is that streams and promises can be a little hard to grok at first. But this is a double edged sword; the practice you’ll get from using them will allow you to better apply them across your other code too.
Configuring the tools
And now you know which tools to use! Of course, it isn’t enough to just know which tools you need. You also need to know how to use them. And as it happens, you’re in luck! I’m currently writing a guide to setting up Webpack and Gulp for your React-based Single Page Application. Get on my newsletter now to make sure you don’t miss it!
In return for your e-mail address, you’ll also immediately receive three print-optimised cheatsheets for React (see preview), ES6 and JavaScript promises – for free!
I will send you useful articles, cheatsheets and code.
One more thing – I love hearing from readers. If you have something to say, send @james_k_nelson a tweet, or send me an e-mail at james@jamesknelson.com. Thanks for reading!
Read More
- Configuring Webpack with Babel in 26 lines
- Unlocking decorators and other ES7 features with Webpack and Babel
- Introduction to Promises
Brunch doesn’t appear among task runners. What do you think of this tool?
I’ve never used it, but it seems to have a much smaller community than Gulp and Grunt.
https://www.npmjs.com/package/rollup ?
Thanks for the article, easy to read and informative.
What do you think of webpack-dev-server? Wouldn’t that be a task runner that replaces Gulp/Grunt?
It does replace Gulp/Grunt in a lot of places, but there are still few things it doesn’t do too well. For example, adding script filenames which include a cache-busting hash to HTML files. My experience is that webpack-dev-server is enough in development, but you’ll need a little more power for production builds.
Have a look at this:
https://github.com/davezuko/react-redux-starter-kit
No need for gulp and it does hashing.
Thanks James, after reading your article, then now i’m sure to use webpack over browserify .. 🙂