Get Up And Running With Grunt.js

May 17, 2013

If you’re anything like myself, you’ve probably had interest in trying to utilize Grunt.js in your projects. Maybe you’ve at least heard of it, or have heard of people using it in their workflows. Unfortunately, the barrier for entry seemed a little bit high for me. Not necessarily technically, but I was confused on how it would actually benefit my workflow.

I’ve been using tools like SASS and Compass for a while now, and recently started to dive into bower for doing front-end package management. Let’s take a look at how to integrate Grunt in a very simple workflow.

Be forwarned, I’m quite new to Grunt in general. Some of the things that are done below can and probably should be optimized. I’d love to hear your feedback!

Why Grunt?

Honest answer? Because it seems like everyone under the sun is using it for doing anything from simple workflow enhancements, to complete production build systems. There’s an extremely active plugin development community, and people involved in the project seem more than willing to help out in answering your questions. There may be other Javascript task runner solutions, but I don’t know of any at the moment that are worth taking a look at.

Our goal

Let’s setup grunt to do the following:

  • monitor our project for changes to SASS files, and use Compass to compile them into CSS.
  • monitor our project for changes to HTML and JS files, and use LiveReload to refresh our page.
  • look into how we can use Grunt to minify and concatenate our Javascript using uglify JS.

The basic grunt setup

Grunt has some basic documentation on their website about setting up your project. First and foremost, Grunt uses Node.js and is installed via npm – node’s package manager. Once you’ve got those installed, you’re ready to install the Grunt CLI globally.

npm install -g grunt-cli

If we were to run a grunt in our project directory, we’d get a message something like this:

A valid Gruntfile could not be found. Please see the getting started guide for more information on how to configure grunt: Fatal error: Unable to find Gruntfile.

We expected that. Let’s create a package.json and a Gruntfile.js in the root of our project.

$ touch package.json Gruntfile.js  


Here are the contents of my very basic package.json file:

"name": "my-project",  
"version": "1.0.0",  
"devDependencies": {  
"grunt": "~0.4.1",  
"grunt-contrib-compass": "~0.2.0",  
"grunt-contrib-watch": "~0.4.3",  
"grunt-contrib-uglify": "~0.2.0",  
"matchdep": "~0.1.2"  

So what exactly does this file do? This tells NPM which dependencies we want to install for our project. The advantages of this are fairly simple. Anyone who collaborates on our project can always be up to date on dependencies and keep our environments in sync. I won’t go into too much detail on these right now. There are plenty of other properties you can place in this file. You can also run npm init to create a boilerplate package file.

Run npm install, and NPM will go fetch these for us and place them in a node_modules folder.

Fantastic! We’ve got packages! Unfortunately they don’t do anything yet, because we haven’t told Grunt what to do with them.


This is where the magic begins to happen. We’ll open up by declaring the “wrapper” function. All Grunt related tasks happen inside of this function.

module.exports = function(grunt) {
  // Do grunt-related things in here

Let’s get started by have the Compass plugin start watching for changes to our SASS files.

module.exports = function(grunt) {

  // load all grunt tasks


    compass: {
      dev: {
        options: {
          config: 'config.rb',
          force: true


First, we’re loading all of our NPM tasks that we specified in our package.json. We’re using the matchdep package to help us do this by iterating over the devDepencies in our json file, and then loading them.

We begin configuring our tasks inside the grunt.initConfig({}) block. You’ll see that we define a compass task with a dev target, and we’re telling Grunt to just load the settings from our compass configuration file – config.rb. In theory, we could also add additional targets here – just append them as separate objects.

For example, you could have a “dist” object in which you would output compressed CSS, or output the CSS to a different folder, etc. You can run specific task targets by using the colon, example: compass:dev or compass:dist.

Let’see what happens when we run grunt!

Warning: Task "default" not found. Use --force to continue.
Aborted due to warnings.

Grunt looks for a default task called “default”, which we haven’t specified. Let’s go ahead and do that now using the grunt.registerTask() function.

This function takes the name of the task you’d like to register, along with an array (or single string) of tasks you want run. Let’s register the default task to run compass and compile our SASS. Add the following after the grunt.initConfig block.

grunt.registerTask('default', 'compass');

When we run grunt again, you’ll see our SASS is compiling!

Running "compass:dev" (compass) task
unchanged sass/app.scss
unchanged sass/normalize.scss

Sweet deal! However, this isn’t really all that useful. We could have gotten the same results by just running a simple compass compile. Let’s keep pushing forward and add a watch task that will monitor changes very similarly to compass watch, but that we can extend to a variety of different plugins.

Go ahead and add a new object that defines our watch task:

module.exports = function(grunt) {

  // load all grunt tasks


    compass: {
      dev: {
        options: {
          config: 'config.rb',
          force: true

    watch: {
      sass: {
        files: ['assets/sass/**/*.scss'],
        tasks: ['compass:dev']
      /* watch and see if our javascript files change, or new packages are installed */
      js: {
        files: ['assets/js/main.js', 'components/**/*.js'],
        tasks: ['uglify']
      /* watch our files for change, reload */
      livereload: {
        files: ['*.html', 'assets/css/*.css', 'assets/images/*', 'assets/js/{main.min.js, plugins.min.js}'],
        options: {
          livereload: true


  grunt.registerTask('default', 'watch');


You’ll see that we’ve added a list of files types for Grunt to look for. We’ve also defined the tasks to run – one for now, but this can be an array of many tasks – when a file has been changed. We’ve modified the default task to run watch instead of compass. Additionally, we’ve added LiveReload functionality with a single line of code.

Note: You will need to install the LiveReload extension for this to easily work without having to include another file in your project. Find the extension for Chrome here.

Let’s re-run grunt and see what we have now.

Running "watch" task

Grunt waits patiently for us to change a file, and when we do, we get successful compilation and a browser reload! Pretty cool.

Only the surface

I’ve only scratched the very surface of what’s possible with Grunt. Check out the vast array of Grunt plguins and start doing everything from compressing javascript to running Jasmine unit tests through Phantom JS.

  • JAaronG

    This is actually exactly what I am trying to do with grunt, so thank you for the article. I had been looking for something to easily minify and concatenate js files, but knowing that it will run compass is a bonus. The one thing that I would add is that after installing everything, my gruntfile.js was in node-modules/grunt, I had to pull it out and put it in my root project folder before grunt would run successfully. I may be setting something up wrong, because I haven’t seen any tutorials that tell you to do this.

    • dmackerman

      You may not have installed Grunt globally. Did you forget the -g flag? You should be able to run grunt in any directory. Only the Grunt tasks are being installed into node_modules

  • Samuel Loveland

    Great getting started guide, especially for us who are still a bit wary of using command line tools. =)

  • Christoph Rumpel

    Great start, thx a lot!

  • Pingback: 使用Grunt构建任务管理脚本 | 易资讯()

  • Tomáš Kout

    Thank you, you made me to start finally!!!

  • Pingback: Website Optimization Workflow – Francisco Deus — Frontend Designer & Developer()

  • Pingback: Website Optimization Workflow: GruntJS | Francisco Deus — Frontend Designer & Developer()

  • Tiago Celestino


  • Cory Logan

    I just wanted to tell you how helpful your article was. I was really having trouble grasping it from the grunt docs, but you’ve lain it out more simply. I feel like this is the perfect step between just getting started, and going to the original documentation!

    Now I’m setting up a grunt task to rsync my project up to my server! Beautiful!


    • dmackerman

      Awesome! I need to update this post, there’s a lot more that I’ve learned how to utilize.

  • Pingback: Grunt JS task runner tutorial roundup | The Developist()

  • Christian Matthew

    what about libsass… can you do one for that?

  • Jim Preston

    I have Foundation 5 and Compass setup and working with OS X Mavericks. I ran $ touch package.json Gruntfile.js in Terminal and it created those files in my _js folder but they are empty files. I copied your package.json code into mine but when I ran npm install it couldn’t read the dependencies and failed to parse json. Frustrating because your instructions are more clear than most.

    • dmackerman

      That’d odd, Jim. I just verified that the package.json does indeed work. Maybe try an `npm init` and add the dependencies manually? Something seems off.

      • Jim Preston

        I got everything working a few days ago, sweet, but now I can’t remember how I solved this problem. I vaguely recall trying nom init at some point and maybe that did it. However, my package.json file has 61 lines and I’m not sure if that is boilerplate or not. It took many days and a bunch of tutorials to get my head around how Foundation, Compass, Bower, Livereload, and Grunt all work together and set them up. Once all the parts are glued together this system is wonderful :-)

  • Corey Bruyere

    Thanks for the straight forward explanation!

  • Amir Moharami


  • Matt Robinson

    Thanks for this tutorial. By far the easiest one to follow that I personally found. :) Cheers

  • Pingback: Quora()

What We Do

We’ll work closely with your team to instill Lean practices for ideation, strategy, design and delivery — practices that are adaptable to every part of your business.

See what Modus can do for you.


We're Hiring!

Join our awesome team of dedicated engineers.