---
title: Grunt and Front End Development
date: 2015-01-19T19:00:00-05:00
author: cc_admin
canonical_url: "https://caffeinecreations.ca/blog/grunt-and-front-end-development/"
section: Blog
---
![Grunt](https://caffeinecreations.ca/uploads/blog/_1920x660_crop_center-center_none_ns/grunt.jpg)

- [Web Development](https://caffeinecreations.ca/blog/category/web-development/), [Code](https://caffeinecreations.ca/blog/category/code/), [ExpressionEngine](https://caffeinecreations.ca/blog/category/expressionengine/), [EE Tips](https://caffeinecreations.ca/blog/category/expressionengine/ee-tips/)

# Grunt and Front End Development

I've heard a lot about [Grunt.js](http://gruntjs.com/) and [Gulp.js](http://gulpjs.com/) over the last year. It's been on my todo list of things to learn. Last week I jumped in with some help from [Tad Ward](https://twitter.com/tadward) via Skype.

I've heard a lot about [Grunt.js](http://gruntjs.com/) and [Gulp.js](http://gulpjs.com/) over the last year. It's been on my todo list of things to learn. Last week I jumped in with some help from [Tad Ward](https://twitter.com/tadward) via Skype.

I initially struggled as I had a project I was about to take on that was using gulp and I misunderstood what was needed. Turned out the files I was given were more examples of what was needed rather than me working with the existing code. Once I got that figured out, I decided to keep looking into both Grunt and Gulp. In the end I decided that Grunt looked easier for more.

I had previously installed Node and [ work in Git](/blog/git-workflow) and Compass on all my projects so most of the requirements to get running were already set up on my system. Currently I've only got a testing project setup but how I see it working is that I open up a command prompt with Administrator permission (requires administrator permissions on Windows, not sure about Mac) and navigate to the project folder and then type npm init. There there will be some prompts that you need to fill out then you bare project is set.

The next step is to run the command:

```

    <button class="absolute flex flex-col right-4 top-4" clipboard="" title="Copy to Clipboard" to="" type="button" x-clipboard.raw="npm install grunt --save-dev" x-data="">
      <svg class="h-6 w-6" viewbox="0 0 64 64" xml:space="preserve" xmlns="http://www.w3.org/2000/svg">
  <path d="M53.98 9.143h-3.97c-.082 0-.155.028-.232.047V5.023C49.778 2.253 47.473 0 44.64 0H10.217C7.384 0 5.08 2.253 5.08 5.023v46.843c0 2.77 2.305 5.023 5.138 5.023h6.037v2.268c0 2.67 2.216 4.843 4.941 4.843H53.98c2.725 0 4.942-2.173 4.942-4.843v-45.17c0-2.671-2.217-4.844-4.942-4.844zM7.11 51.866V5.023c0-1.649 1.394-2.991 3.106-2.991H44.64c1.712 0 3.106 1.342 3.106 2.99v46.844c0 1.649-1.394 2.991-3.106 2.991H10.217c-1.712 0-3.106-1.342-3.106-2.99zm49.778 7.29c0 1.551-1.306 2.812-2.91 2.812H21.195c-1.604 0-2.91-1.26-2.91-2.811v-2.268H44.64c2.833 0 5.138-2.253 5.138-5.023V11.128c.077.018.15.047.233.047h3.968c1.604 0 2.91 1.26 2.91 2.811v45.17z"></path>
  <path d="M38.603 13.206H16.254a1.015 1.015 0 1 0 0 2.032h22.35a1.015 1.015 0 1 0 0-2.032zM38.603 21.333H16.254a1.015 1.015 0 1 0 0 2.032h22.35a1.015 1.015 0 1 0 0-2.032zM38.603 29.46H16.254a1.015 1.015 0 1 0 0 2.032h22.35a1.015 1.015 0 1 0 0-2.032zM28.444 37.587h-12.19a1.015 1.015 0 1 0 0 2.032h12.19a1.015 1.015 0 1 0 0-2.032z"></path>
</svg>

          <div class="sr-only">
            Copy to clipboard
          </div>
    </button>
    ```javascript

      npm install grunt --save-dev
    
```
  
```

The --save-dev installs grunt to this particular project. Once this is done, you then install the other grunt tasks that you want to run. The ones that I want to work with are [Compass](https://www.npmjs.com/package/grunt-contrib-compass), [Uglify](https://www.npmjs.com/package/grunt-contrib-uglify), [Watch](https://www.npmjs.com/package/grunt-contrib-watch) and [Concat](https://github.com/gruntjs/grunt-contrib-concat). Next type the following commands:

```

    <button class="absolute flex flex-col right-4 top-4" clipboard="" title="Copy to Clipboard" to="" type="button" x-clipboard.raw="npm install grunt-contrib-compass --save-dev
npm install grunt-contrib-uglify --save-dev
npm install grunt-contrib-watch --save-dev
npm install grunt-contrib-concat --save-dev" x-data="">
      <svg class="h-6 w-6" viewbox="0 0 64 64" xml:space="preserve" xmlns="http://www.w3.org/2000/svg">
  <path d="M53.98 9.143h-3.97c-.082 0-.155.028-.232.047V5.023C49.778 2.253 47.473 0 44.64 0H10.217C7.384 0 5.08 2.253 5.08 5.023v46.843c0 2.77 2.305 5.023 5.138 5.023h6.037v2.268c0 2.67 2.216 4.843 4.941 4.843H53.98c2.725 0 4.942-2.173 4.942-4.843v-45.17c0-2.671-2.217-4.844-4.942-4.844zM7.11 51.866V5.023c0-1.649 1.394-2.991 3.106-2.991H44.64c1.712 0 3.106 1.342 3.106 2.99v46.844c0 1.649-1.394 2.991-3.106 2.991H10.217c-1.712 0-3.106-1.342-3.106-2.99zm49.778 7.29c0 1.551-1.306 2.812-2.91 2.812H21.195c-1.604 0-2.91-1.26-2.91-2.811v-2.268H44.64c2.833 0 5.138-2.253 5.138-5.023V11.128c.077.018.15.047.233.047h3.968c1.604 0 2.91 1.26 2.91 2.811v45.17z"></path>
  <path d="M38.603 13.206H16.254a1.015 1.015 0 1 0 0 2.032h22.35a1.015 1.015 0 1 0 0-2.032zM38.603 21.333H16.254a1.015 1.015 0 1 0 0 2.032h22.35a1.015 1.015 0 1 0 0-2.032zM38.603 29.46H16.254a1.015 1.015 0 1 0 0 2.032h22.35a1.015 1.015 0 1 0 0-2.032zM28.444 37.587h-12.19a1.015 1.015 0 1 0 0 2.032h12.19a1.015 1.015 0 1 0 0-2.032z"></path>
</svg>

          <div class="sr-only">
            Copy to clipboard
          </div>
    </button>
    ```javascript

      npm install grunt-contrib-compass --save-dev
npm install grunt-contrib-uglify --save-dev
npm install grunt-contrib-watch --save-dev
npm install grunt-contrib-concat --save-dev
    
```
  
```

Next up we need to create a [gruntfile](http://gruntjs.com/api/grunt.file) which loads the commands and tasks that will be run. Through a lot of trial and error my working gruntfile is here:

```

    <button class="absolute flex flex-col right-4 top-4" clipboard="" title="Copy to Clipboard" to="" type="button" x-clipboard.raw="module.exports = function(grunt) {
 
// Project configuration.
grunt.initConfig({
 pkg: grunt.file.readJSON('package.json'),
// CONFIG ===================================/
 
	watch: {
		compass: {
			files: ['**/*.{scss,sass}'],
			tasks: ['compass:dev']
		},
		js: {
			files: ['build/js/**/*.js', 'bower_components/*.js'],
			tasks: ['uglify', 'concat']
		},
 
		livereload: {
			options:{livereload:true},
			files:['css/*', '*.html', 'templates/**/*.html'],
		}
	},
 
	compass: {
		dev: {
		   options: {              
		       sassDir: ['build/sass'],
		       cssDir: ['css'],
		       environment: 'development'
		   }
		},
		prod: {
		   options: {              
		       sassDir: ['build/sass'],
		       cssDir: ['css'],
		       environment: 'production',
		       outputStyle: 'compact'
		   }
		},
 
	},
 
	uglify: {
		dev:{
			options:{
				compress: {
					drop_console: false
				}
			},
			files: {
		      'js/main.min.js': [
		      'build/js/*.js', 
		      'build/js/**/*.js'
		      ]
		  }
		},
		prod: {
			options:{
				compress: {
					drop_console: true
				}
			},
			files: {
		      'js/main.min.js': [
		      'build/js/*.js', 
		      'build/js/**/*.js'
		      ]
		  }
		},
 
	},
 
	// concatenate js and output unminified.
	concat: {
	    options: {
	      separator: '\r\n\r\n /******/ \r\n\r\n',
	    },
	    dist: {
	      src: ['build/js/*.js', 'build/js/**/*.js'],
	      dest: 'js/main.js',
	    },
	},
 
 
 
});
 
// DEPENDENT PLUGINS =========================/
 
grunt.loadNpmTasks('grunt-contrib-watch');
 
grunt.loadNpmTasks('grunt-contrib-compass');
 
grunt.loadNpmTasks('grunt-contrib-uglify');
 
grunt.loadNpmTasks('grunt-contrib-concat');
 
 
// TASKS =====================================/
 
grunt.registerTask('default', ['compass:dev' , 'uglify:dev' , 'concat', 'watch']);
 
};" x-data="">
      <svg class="h-6 w-6" viewbox="0 0 64 64" xml:space="preserve" xmlns="http://www.w3.org/2000/svg">
  <path d="M53.98 9.143h-3.97c-.082 0-.155.028-.232.047V5.023C49.778 2.253 47.473 0 44.64 0H10.217C7.384 0 5.08 2.253 5.08 5.023v46.843c0 2.77 2.305 5.023 5.138 5.023h6.037v2.268c0 2.67 2.216 4.843 4.941 4.843H53.98c2.725 0 4.942-2.173 4.942-4.843v-45.17c0-2.671-2.217-4.844-4.942-4.844zM7.11 51.866V5.023c0-1.649 1.394-2.991 3.106-2.991H44.64c1.712 0 3.106 1.342 3.106 2.99v46.844c0 1.649-1.394 2.991-3.106 2.991H10.217c-1.712 0-3.106-1.342-3.106-2.99zm49.778 7.29c0 1.551-1.306 2.812-2.91 2.812H21.195c-1.604 0-2.91-1.26-2.91-2.811v-2.268H44.64c2.833 0 5.138-2.253 5.138-5.023V11.128c.077.018.15.047.233.047h3.968c1.604 0 2.91 1.26 2.91 2.811v45.17z"></path>
  <path d="M38.603 13.206H16.254a1.015 1.015 0 1 0 0 2.032h22.35a1.015 1.015 0 1 0 0-2.032zM38.603 21.333H16.254a1.015 1.015 0 1 0 0 2.032h22.35a1.015 1.015 0 1 0 0-2.032zM38.603 29.46H16.254a1.015 1.015 0 1 0 0 2.032h22.35a1.015 1.015 0 1 0 0-2.032zM28.444 37.587h-12.19a1.015 1.015 0 1 0 0 2.032h12.19a1.015 1.015 0 1 0 0-2.032z"></path>
</svg>

          <div class="sr-only">
            Copy to clipboard
          </div>
    </button>
    ```javascript

      module.exports = function(grunt) {
 
// Project configuration.
grunt.initConfig({
 pkg: grunt.file.readJSON('package.json'),
// CONFIG ===================================/
 
	watch: {
		compass: {
			files: ['**/*.{scss,sass}'],
			tasks: ['compass:dev']
		},
		js: {
			files: ['build/js/**/*.js', 'bower_components/*.js'],
			tasks: ['uglify', 'concat']
		},
 
		livereload: {
			options:{livereload:true},
			files:['css/*', '*.html', 'templates/**/*.html'],
		}
	},
 
	compass: {
		dev: {
		   options: {              
		       sassDir: ['build/sass'],
		       cssDir: ['css'],
		       environment: 'development'
		   }
		},
		prod: {
		   options: {              
		       sassDir: ['build/sass'],
		       cssDir: ['css'],
		       environment: 'production',
		       outputStyle: 'compact'
		   }
		},
 
	},
 
	uglify: {
		dev:{
			options:{
				compress: {
					drop_console: false
				}
			},
			files: {
		      'js/main.min.js': [
		      'build/js/*.js', 
		      'build/js/**/*.js'
		      ]
		  }
		},
		prod: {
			options:{
				compress: {
					drop_console: true
				}
			},
			files: {
		      'js/main.min.js': [
		      'build/js/*.js', 
		      'build/js/**/*.js'
		      ]
		  }
		},
 
	},
 
	// concatenate js and output unminified.
	concat: {
	    options: {
	      separator: '\r\n\r\n /******/ \r\n\r\n',
	    },
	    dist: {
	      src: ['build/js/*.js', 'build/js/**/*.js'],
	      dest: 'js/main.js',
	    },
	},
 
 
 
});
 
// DEPENDENT PLUGINS =========================/
 
grunt.loadNpmTasks('grunt-contrib-watch');
 
grunt.loadNpmTasks('grunt-contrib-compass');
 
grunt.loadNpmTasks('grunt-contrib-uglify');
 
grunt.loadNpmTasks('grunt-contrib-concat');
 
 
// TASKS =====================================/
 
grunt.registerTask('default', ['compass:dev' , 'uglify:dev' , 'concat', 'watch']);
 
};
    
```
  
```

What happens here is that the watch command Sets up files to watch and tasks to do. First it runs compass so that my Sass files get processed and output to the folders indicated in the Compass command below. Next in Watch js is uglified, i.e. compressed and concatenated. Lastly I use Concat to concatenate all the js files and output a version that is not minified. The minified version will be used in production. Line 76 adds line breaks between concatenated files for easier readability.

I do a lot of work for other agencies and feel that I should also provide them with a readable version of the javascript which this accomplishes. Now grunt outputs a main.js and a main.min.js file to the /js/ folder.

Back to the Watch command on line 18 we see:

```

    <button class="absolute flex flex-col right-4 top-4" clipboard="" title="Copy to Clipboard" to="" type="button" x-clipboard.raw="livereload: {
	options:{livereload:true},
	files:['css/*', '*.html', 'templates/**/*.html'],
}" x-data="">
      <svg class="h-6 w-6" viewbox="0 0 64 64" xml:space="preserve" xmlns="http://www.w3.org/2000/svg">
  <path d="M53.98 9.143h-3.97c-.082 0-.155.028-.232.047V5.023C49.778 2.253 47.473 0 44.64 0H10.217C7.384 0 5.08 2.253 5.08 5.023v46.843c0 2.77 2.305 5.023 5.138 5.023h6.037v2.268c0 2.67 2.216 4.843 4.941 4.843H53.98c2.725 0 4.942-2.173 4.942-4.843v-45.17c0-2.671-2.217-4.844-4.942-4.844zM7.11 51.866V5.023c0-1.649 1.394-2.991 3.106-2.991H44.64c1.712 0 3.106 1.342 3.106 2.99v46.844c0 1.649-1.394 2.991-3.106 2.991H10.217c-1.712 0-3.106-1.342-3.106-2.99zm49.778 7.29c0 1.551-1.306 2.812-2.91 2.812H21.195c-1.604 0-2.91-1.26-2.91-2.811v-2.268H44.64c2.833 0 5.138-2.253 5.138-5.023V11.128c.077.018.15.047.233.047h3.968c1.604 0 2.91 1.26 2.91 2.811v45.17z"></path>
  <path d="M38.603 13.206H16.254a1.015 1.015 0 1 0 0 2.032h22.35a1.015 1.015 0 1 0 0-2.032zM38.603 21.333H16.254a1.015 1.015 0 1 0 0 2.032h22.35a1.015 1.015 0 1 0 0-2.032zM38.603 29.46H16.254a1.015 1.015 0 1 0 0 2.032h22.35a1.015 1.015 0 1 0 0-2.032zM28.444 37.587h-12.19a1.015 1.015 0 1 0 0 2.032h12.19a1.015 1.015 0 1 0 0-2.032z"></path>
</svg>

          <div class="sr-only">
            Copy to clipboard
          </div>
    </button>
    ```javascript

      livereload: {
	options:{livereload:true},
	files:['css/*', '*.html', 'templates/**/*.html'],
}
    
```
  
```

This is pure gold. Before I got this working I had gone after a red herring, grunt-browser-sync, which would never install and led to much frustration. Once I found the docs for [Live Reload](https://github.com/gruntjs/grunt-contrib-watch#optionslivereload) and set it up to reload css/js/html on change I was set. This command actually watches the CSS file for change. So once compass processes the Sass files and outputs the new CSS file then the browser reloads. There is a slight delay, but it is nice not having to manually refresh the page. To get it to work on your local files you need to either add a bit of javascript to the site or use a [browser extension](http://feedback.livereload.com/knowledgebase/articles/86242-how-do-i-install-and-use-the-browser-extensions-) (Safari, Chrome, FireFox). Now all I do is make a change to the sass files, wait a second and the change is reflected on the site locally. No need for me to manually refresh the browser. This is brilliant. \*UPDATE\* - changed files for livereload and now works when developing in ExpressionEngine. I have my templates folder at public\_html/templates - the /\*\*/ is needed so that it will look for all html files inside all subdirectories.

When it's time to start development in the in the Command Prompt all you do is type grunt and then it performs the tasks set up on line 101.

```

    <button class="absolute flex flex-col right-4 top-4" clipboard="" title="Copy to Clipboard" to="" type="button" x-clipboard.raw="grunt.registerTask('default', ['compass:dev' , 'uglify:dev' , 'concat', 'watch']);" x-data="">
      <svg class="h-6 w-6" viewbox="0 0 64 64" xml:space="preserve" xmlns="http://www.w3.org/2000/svg">
  <path d="M53.98 9.143h-3.97c-.082 0-.155.028-.232.047V5.023C49.778 2.253 47.473 0 44.64 0H10.217C7.384 0 5.08 2.253 5.08 5.023v46.843c0 2.77 2.305 5.023 5.138 5.023h6.037v2.268c0 2.67 2.216 4.843 4.941 4.843H53.98c2.725 0 4.942-2.173 4.942-4.843v-45.17c0-2.671-2.217-4.844-4.942-4.844zM7.11 51.866V5.023c0-1.649 1.394-2.991 3.106-2.991H44.64c1.712 0 3.106 1.342 3.106 2.99v46.844c0 1.649-1.394 2.991-3.106 2.991H10.217c-1.712 0-3.106-1.342-3.106-2.99zm49.778 7.29c0 1.551-1.306 2.812-2.91 2.812H21.195c-1.604 0-2.91-1.26-2.91-2.811v-2.268H44.64c2.833 0 5.138-2.253 5.138-5.023V11.128c.077.018.15.047.233.047h3.968c1.604 0 2.91 1.26 2.91 2.811v45.17z"></path>
  <path d="M38.603 13.206H16.254a1.015 1.015 0 1 0 0 2.032h22.35a1.015 1.015 0 1 0 0-2.032zM38.603 21.333H16.254a1.015 1.015 0 1 0 0 2.032h22.35a1.015 1.015 0 1 0 0-2.032zM38.603 29.46H16.254a1.015 1.015 0 1 0 0 2.032h22.35a1.015 1.015 0 1 0 0-2.032zM28.444 37.587h-12.19a1.015 1.015 0 1 0 0 2.032h12.19a1.015 1.015 0 1 0 0-2.032z"></path>
</svg>

          <div class="sr-only">
            Copy to clipboard
          </div>
    </button>
    ```html

      grunt.registerTask('default', ['compass:dev' , 'uglify:dev' , 'concat', 'watch']);
    
```
  
```

Now when I'm ready to move the code to production I can run different commands for example **grunt compass:prod** will run the compass task but instead of adding line comments and keeping the css readable everything will be compressed with no comments. Additionally running **grunt uglify:prod** will take care of the js files and remove all console.logs, which are not really necessary on production.

Assuming you have a number of tasks that you like to use on every site, this whole process will be even easier. All you need to do is copy package.json and gruntfile.js of an existing project into a new project folder. Then open up your command prompt with administrator permission and navigate to the new folder and then enter this command:

```

    <button class="absolute flex flex-col right-4 top-4" clipboard="" title="Copy to Clipboard" to="" type="button" x-clipboard.raw="npm install" x-data="">
      <svg class="h-6 w-6" viewbox="0 0 64 64" xml:space="preserve" xmlns="http://www.w3.org/2000/svg">
  <path d="M53.98 9.143h-3.97c-.082 0-.155.028-.232.047V5.023C49.778 2.253 47.473 0 44.64 0H10.217C7.384 0 5.08 2.253 5.08 5.023v46.843c0 2.77 2.305 5.023 5.138 5.023h6.037v2.268c0 2.67 2.216 4.843 4.941 4.843H53.98c2.725 0 4.942-2.173 4.942-4.843v-45.17c0-2.671-2.217-4.844-4.942-4.844zM7.11 51.866V5.023c0-1.649 1.394-2.991 3.106-2.991H44.64c1.712 0 3.106 1.342 3.106 2.99v46.844c0 1.649-1.394 2.991-3.106 2.991H10.217c-1.712 0-3.106-1.342-3.106-2.99zm49.778 7.29c0 1.551-1.306 2.812-2.91 2.812H21.195c-1.604 0-2.91-1.26-2.91-2.811v-2.268H44.64c2.833 0 5.138-2.253 5.138-5.023V11.128c.077.018.15.047.233.047h3.968c1.604 0 2.91 1.26 2.91 2.811v45.17z"></path>
  <path d="M38.603 13.206H16.254a1.015 1.015 0 1 0 0 2.032h22.35a1.015 1.015 0 1 0 0-2.032zM38.603 21.333H16.254a1.015 1.015 0 1 0 0 2.032h22.35a1.015 1.015 0 1 0 0-2.032zM38.603 29.46H16.254a1.015 1.015 0 1 0 0 2.032h22.35a1.015 1.015 0 1 0 0-2.032zM28.444 37.587h-12.19a1.015 1.015 0 1 0 0 2.032h12.19a1.015 1.015 0 1 0 0-2.032z"></path>
</svg>

          <div class="sr-only">
            Copy to clipboard
          </div>
    </button>
    ```javascript

      npm install
    
```
  
```

Voila everything is installed and you are set to start developing. Comments and suggestions on tasks that may be useful to me or better approaches are appreciated.

## More Articles You May Like

[![How to Choose a CMS Thumbnail](https://caffeinecreations.ca/uploads/hero/_680x320_crop_center-center_65_none_ns/choice.jpg)### How to Choose a CMS](https://caffeinecreations.ca/blog/how-to-choose-a-cms/)

[![Closing Comments in Emmet with Visual Studio Code Thumbnail](https://caffeinecreations.ca/uploads/blog/_680x320_crop_center-center_65_none_ns/emmet-logo.jpg)### Closing Comments in Emmet with Visual Studio Code](https://caffeinecreations.ca/blog/closing-comments-in-emmet-with-visual-studio-code/)

[![Per Environment robots.txt Thumbnail](https://caffeinecreations.ca/uploads/blog/_680x320_crop_center-center_65_none_ns/robots-txt.png)### Per Environment robots.txt](https://caffeinecreations.ca/blog/per-environment-robots-txt/)
