I have spent time using Grunt. I have spent a bit more time with Gulp. But now, after being shown the capabilities of using NPM scripts, I am making the switch to NPM as my build tool of choice.

The decision to convert my workflow boils down to approximately two reasons - the overall feel and the more vast selection of packages.

The Feel

Don’t act like I’m the only developer who has made a decision based on how something feels. Luckily, I can loosely quantify this reason.

I was already using NPM and creating a package.json file. I would see the base ‘test’ script but neglect what it was actually doing. Prior to a friend showing me how they converted their workflow away from Gulp in the same way, I never understood the amount of power that the package file actually carried.

For the same reason I prefer writing vanilla JavaScript over jQuery, I now prefer writing NPM scripts over Gulp or Grunt tasks. Why add the unnecessary layer of abstraction? This native implementation feels cleaner and more lightweight.

There are some default scripts provided, such as test, but NPM allows the creation of custom scripts. And another great draw to these custom scripts is their lifecycle hooks. For every script I create I have access to pre and post hooks that will run before and after the primary script.

For example, I have a build script that handles some compilation of styles and scripts. But before building I want to ensure that all of my tests pass. I can create a prebuild script which will execute before my build runs. And similarly, my postbuild script will run after the initial build executes. With custom scripts and their hooks, there is a tremendous amount of flexibility in creating your workflow.

  "scripts": {
    "prebuild": "npm run test",
    "build": "npm-run-all --parallel build:*",
    "build:styles": "postcss -u autoprefixer --no-map -r ./dist/styles/*.css",
    "build:docs": "docco -o ./docs/ ./src/js/**/*",
    "build:images": "imagemin ./src/images/**/* -o ./dist/images/**/*",
    "build:minifycss": "minify ./dist/styles/app.css",
    "build:minifyjs": "minify ./dist/js/app.js",
    "test": "mocha"

Another reason I like writing these scripts is the easy integration with terminal commands. For example, copying, deleting, or creating files can be done in a script with the same commands that you would use in the terminal. I have found some minor use for this but it, again, why install a new package to do something that I can handle with a terminal command.

  "scripts": {
    "watch": "npm-run-all --parallel watch:*",
    "watch:images": "cp -r ./src/images/ ./dist/images/"

The caveat to this is if I try to run my package file on a Windows machine. The terminal on Mac and command prompt on Windows use different commands. This would cause an error. But honestly, when dealing with my own personal project, not one that concerns me. When working with a package file that may end up being shared across operating systems, it may be best to leverage a package for broader support.

But I digress.

Ultimately, this all feels better to me. Debugging is simplified by removing the extra layer, I’d imagine this runs slightly quicker, though I have not benchmarked it at all, and it allows me to better understand and utilize the tools I was already using before adding more to the mix. So yes, this all feels better to me.

All the Packages!

More times than I can really recall, I have wanted to use an NPM package that did not have a Gulp equivalent. Whether I was trying to integrate a new linter, some image minification, or something entirely random and pointless, I was at the mercy of additional developers porting native packages to Gulp. And I hate mercy.

When I would find Gulp packages they were often times poorly documented in comparison to the native equivalent. Some Gulp extractions would port the full package while some would only expose certain parts of the original package. This resulted in bouncing back and forth between sets of documentation and trial and error. These tools are meant to improve your process not slow you down further.

NPM Packages versus Gulp and Grunt

By converting my workflow to NPM scripts I now have full access to every package on NPM. This gives me more resources and generally better documentation. Additionally, this reduces the amount of debugging I have to do.


Having converted my workflow over to using npm as my build tool feels like a cleaner more flexible solution. I have really enjoyed working with the different hooks and seeing how much I can automate - a feeling I never had with Grunt or Gulp. The process feels more direct and I save time in debugging and searching through documentation. This may not be the best solution for everybody but I would encourage taking a look and see if it benefits your workflow the same as it did for me.

To review my ever-evolving project template visit my Github: Project Template on Github

2,603 3 19