Makefiles for Frontend

An alternative to npm package scripts

Dave Honneffer
Jan 27, 2020 · 4 min read

While npm scripts are built-in and work well for a variety of situations, there are times when another task runner might make more sense.

My goal is not to suggest you always use Makefiles instead of npm scripts — but rather show possible advantages and provide an introduction. Doesn’t hurt to have one more tool in the toolbox, right?


  • Makefiles can easily include documentation, or even self-document
  • Makefiles support inheritance — this means you can easily share a common set of commands across projects in your team — we even distribute our commands in an npm module!
  • Make can help consolidate any shell scripts you’re using
  • You can use npm-scripts and Make together, these aren’t mutually exclusive approaches
  • Make includes a dry run flag (), which is extremely helpful when creating more complex scripts
lots of old hammers — because Make is ‘one more tool in the toolbox’
lots of old hammers — because Make is ‘one more tool in the toolbox’

Quick intro

In this section we’ll do some comparisons between NPM scripts and what they’d look like when translated to Make. We’ll also quickly review the structure of a Make rule.

Basic rules

Here we have a typical npm script (in our ) to run Rollup. We could run this with or .

This same command would look like this 👆 in Make (in a file named ), and can be run with .

We do need to use a task-runner (either or ) to actually run our commands, but things still work the same outside of that small change. [1]

Breaking it down

A single script in Make is called a rule, and rules have the following form:

  • Note that the indentation for commands — must be a tab. [2]
  • A target can have as many commands as needed, they are all run in a separate shell. [3]
  • Prereqs are often another target, but can be used to set up variables for your rule. Examples of these are both shown below.

Running multiple rules

To run multiple commands with npm package scripts, you might do something like this.

The syntax for this in Make is shown below — note Make will automatically short-circuit any failures, so if the command fails, won’t run; the same behavior we’re getting above via our .

Recipe-level variables

These can be used by Make, but not by any programs we run. They’re useful in cases where we’d just be adding another flag to an already existing command — as we often do when running in development vs. production environments.

In the example below:

  • running will first set the variable to , then will run the target as a prereq (meaning of course it will run the and tasks) — this means the task would be run as
  • running would just run and tasks, meaning won’t be set to anything — thus the task would be run as
  • we can use in our Make recipes, but Rollup and Sass won’t know about as an environment variable

If we do want our recipes to have the environment variable set, that’s an easy fix! We just add like so:

An example Makefile

You can also view a gist for this example instead.



[1] — We can directly run the files as well if this is preferred.

[2] — It actually doesn’t have to be a tab, but you should leave the Make-default alone. If you really want to change this, you can look into the variable.

[3] — The variable can work around this, but is generally advised against.

[*] — Yes, we should have set up

Photo by Sucrebrut on Unsplash Blog – Product, Design, and Tech

Posts from the people building the world’s greatest…