Why and how to use Grunt

Sometimes I'm a bit lazy, especially when it comes to repetitive tasks. I just don't like doing the same things over and over again. I'm more into writing code, thinking about solutions and things like that - wasting time by doing the same thing the tenth time isn't really my business.

That's where Grunt comes into play.

What is Grunt?

Pretty simple: a Task Runner. So basically, it's all about the automation of (repetitive) tasks.

With this in mind consider the following scenarios:

  • If you know about the advantages of SASS (or LESS) and use it very frequently, everytime you change your SASS/LESS files you have to recompile them manually.
  • Before releasing your product you may want to increase its performance by minifying your javascript and css code - you may use some online tools like cssminifier.com and similar.
  • Since you (and your team) want to write good code you're probably using linters like for your css, js and so on. Those linters have to be run after every single change in the corresponding files.
  • To ship your product you'd like to compress it into a single archive. To do this, you have to mark all files which belong to your application (or whatever you've written) and zip them. For every release. And, even worse, sometimes you have to deal with these annoying .DS_Store (OS X) or Thumbs.db (Windows) files within your archives.
  • To increase the performance of your app even further you'd like to concatenate all your js or css files into a single file. Honestly, I have no idea how to do that without a task runner, sorry.
  • If you're developing web app or even a website, most of the times you change something you have to reload the page by pressing refresh or hitting ctrl + r (f5 if you have long fingers). On every device, on every browser.
  • ... and many more (compiling Coffee or Babel to javascript, unit testing, optimizing images, autoprefixing css, ...)

If you do all of these things, you're awesome, since you get the idea of modern web development (okay, refreshing your browser isn't necessarily "modern", but you know what I mean). But without a task runner you have to do these tasks every fucking time on your own. And during the development of a web app or a website, you'll do these things very often. Compiling SASS to CSS, refreshing browser(s), linting your files and so on isn't necessarily productive - these things are just necessities. Therefore automating these tasks means a massive increase in productivity.

When to use it?

If you're developing websites or web apps Grunt will almost always be an enrichment for you. I'm using Grunt for a quite a while now - for websites, web apps and even things like HTML prototypes. The latter mainly to make use of SASS.

After some time you got a bunch of different Grunt configurations (see below for more information) and it comes down to copying configurations.

How to use it?

Grunt is built on NodeJS - its only requirement.

Grunt doesn't include any tasks. They come in form of plugins, which needs to be configured and... well, that's it.

Let's say we want to Grunt to watch our CSS files and automatically autoprefix them. Let's assume a very simple project structure:

/index.html
/style.css

For using Grunt we need two additional files:

  • one for Node/npm (package.json)
  • the Grunt configuration file (Gruntfile.js)

The package.json is used for the node package manager, npm, and just contains a list of our dependencies:

{
  "name": "grunt-example",
  "author": "your-name",
  "private": true,
  "devDependencies": {
  }
}

As you see I've left out the dependencies. Just for one reason; it's easier to install dependencies directly with npm than to manually write them down. More specifically: just type npm install [package-name] --save-dev into your CLI (your project folder must be your current working directory) and you're done. The --save-dev flag is just to save the package to your package.json.

We need the following packages:

  • A task to watch files for changes: grunt-contrib-watch
  • A task to autoprefix our css: grunt-autoprefixer (This grunt plugin is marked as deprecated and moved to grunt-postcss, which can do even more things than "just" autoprefixing. But because of simplicity we'll still use it.)
  • And, of course, grunt itself.

After running

  • npm install grunt-contrib-watch --save-dev
  • npm install grunt-autoprefixer --save-dev
  • npm install grunt --save-dev

Our package.json should look like this:

{
  "name": "grunt-example",
  "author": "your-name",
  "private": true,
  "devDependencies": {
    "grunt": "^0.4.5",
    "grunt-autoprefixer": "^3.0.3",
    "grunt-contrib-watch": "^0.6.1"
  }
}

Now our project directory should look like this:

/index.html
/style.css
/node_modules
/Gruntfile.js
/package.json

There's absolutely nothing special about the index.html or style.css; these files are just "ordinary" html / css files:

// index.html
<!DOCTYPE html>
<html>

<head>
    <meta charset="UTF-8">
    <title>Grunt test</title>

    <link href="style.css" type="text/css" rel="stylesheet">
</head>

<body>
    <p>Hello World.</p>
</body>
</html>

The style.css can be left empty for now.

That's our structure and the only thing left is to configure our tasks. Hence we need to tell our watch task what files to watch and what to do if a file has changed. Task configuration is done within our Gruntfile.js:

'use strict';

module.exports = function(grunt) {

    // Tell Grunt what tasks to load
    grunt.loadNpmTasks('grunt-contrib-watch');
    grunt.loadNpmTasks('grunt-autoprefixer');

    // Task configuration
    grunt.initConfig({
        // The watch task
        watch: {
            // Watch our css files for changes
            css: {
                files: ['style.css'], // what file to watch
                tasks: ['autoprefixer'] // what task to execute on change
            },
            options: {
                // To prevent checking files for change after the autoprefixer task is done
                spawn: false
            }
        },

        // Autoprefix task
        autoprefixer: {
            dev: {
                src: 'style.css', // source file
                dest: 'style.css' // destionation file
            }
        }
    });

    // Default task when the grunt command is called
    grunt.registerTask('default', ['watch']);

}

And, well, that's it. Now our css file is autoprefixed automatically on every change:

Note: It's definitely not the best idea to change opened files (and the transition effect is totally useless in this case). But this was just to demonstrate what Grunt does for you.

A more "realistic" example would be using SASS and BrowserSync. SASS for "a better CSS" and BrowserSync to sychronize file changes (and interactions) across multiple browsers (and even devices!).

For this we need different packages and a different Gruntfile:

The package.json:

{
  "name": "grunt-example",
  "author": "your-name",
  "private": true,
  "devDependencies": {
    "grunt": "^0.4.5",
    "grunt-browser-sync": "^2.1.3",
    "grunt-contrib-sass": "^0.9.2",
    "grunt-contrib-watch": "^0.6.1"
  }
}

Note: After changing your package.json you have to run npm install to install our new packages. (Fun fact: you can automate this with Grunt)

And the Gruntfile.js:

'use strict';

module.exports = function(grunt) {

    // Tell Grunt what tasks to load
    grunt.loadNpmTasks('grunt-contrib-watch');
    grunt.loadNpmTasks('grunt-contrib-sass');
    grunt.loadNpmTasks('grunt-browser-sync');

    // Task configuration
    grunt.initConfig({
        // The watch task
        watch: {
            // Watch our scss files for changes and translate them to css on
            // change
            sass: {
                files: ['style.scss'],
                tasks: ['sass']
            },
            options: {
                spawn: false
            }
        },
        
        // The sass task
        sass: {
            dist: {
                files: {
                    // Destination : Source
                    'style.css': 'style.scss'
                }
            }
        },

        // Browser sync task
        browserSync: {
            dev: {
                // What files to inject
                bsFiles: {
                    src: ['style.css']
                },
                options: {
                    "watchTask": true,
                    "proxy": "nehalem/test/gruntt"
                }
            }
        }
    });

    // Default task when the grunt command is called
    grunt.registerTask('default', [
        'browserSync',
        'watch'
    ]);
}

Note: If you have a lot of grunt tasks to load, it may be a good idea to use a plugin instead of writing grunt.loadNpmTasks(...) for every task.

Looks a bit more complicated, but the result is really cool:

  • Our SASS is compiled to CSS everytime our .scss file is saved/changed
  • Every CSS changes are automatically injected in the browser

In fact I didn't even move my mouse or changed window focus one time - I just wrote some lines and everything else happened automatically.

In an upcoming project on which I'm currently working on, a WordPress Theme boilerplate, Grunt does even more for the developer, like minify your images, create a distributable version of the theme, validate your css and js, etc. - just to simplify and speed up development. But I'll talk more about this project and its numerous Grunt tasks as soon as possible (basically as soon as I've solved the naming problem).

What about...

I do know about Gulp (or LESS, LiveReload, etc.). And I do not say that they are bad or worse than the ones I've mentioned.

This post is not about comparing things, but to show you how to speed up development and how to make your life as a developer a bit easier by using task automation.