Chris on Code
This tutorial is out of date and no longer maintained.
During our web development workflows, there are many tasks that we have to repeat. These are tasks things like minifying JavaScript and CSS files, unit testing, linting your files to check for errors, compiling CSS preprocessor files (LESS, SASS), and much more.
Grunt is a task runner. This means that those repetitive tasks we deal with in our daily workflow get automated. This will be a simple look at how to get up and running with Grunt. We will look at doing the basic tasks:
There are many tutorials out there that talk about the great things you can do with Grunt. While great, sometimes they come with confusing and fully-fledged configurations that are hard to understand for someone just getting into Grunt. This will be a guide in the basics of using Grunt and creating an incredibly simple and easy setup to handle the tasks mentioned above. This setup will teach us the basics but will let us think about how we can expand Grunt for future more advanced uses.
To use Grunt, we will need to have Node.js installed. Don’t worry, you can use Grunt in any application you want, whether it is a Node app, a PHP app, WordPress, or just a plain old HTML/CSS/JS site. Node and its package manager (npm) are used to pull in the packages we need. Each package will have a different function like minifying or linting.
If you don’t already have Node installed on your computer, go ahead and grab it and we’ll start working with Grunt. To make sure that you have Node and npm installed, go into your command line and type node -v
and npm -v
. If you see version numbers, then you’re ready to go!
We’ll keep our project files very simple. This will be the file structure for our samples. Go ahead and create the folders and files shown. We’ll leave our files blank and start adding to them later.
- dist // will hold all our of final files (minified files)
----- css
----- js
- src // will hold all of our original files
----- css
---------- style.css
---------- pretty.less
----- js
---------- magic.js
Gruntfile.js // our Grunt configuration
package.json // our npm pacakge configuration (how we pull in packages)
Notice how we have a src
folder and a dist
folder. We will be doing our work inside the src
folder and Grunt will minify those files and save them into the dist
folders. The files in dist
are the ones that we will use for our final site.
When using npm, we define the packages we need in a package.json
file. Let’s go into that file and add the packages we need. We’ll explain what each package does also.
{
"name": "grunt-getting-started",
"version": "0.1.0",
"devDependencies": {
"grunt": "~0.4.4",
"grunt-contrib-jshint": "latest",
"jshint-stylish": "latest",
"grunt-contrib-uglify": "latest",
"grunt-contrib-less": "latest",
"grunt-contrib-cssmin": "latest",
"grunt-contrib-watch": "latest"
}
}
Here we have defined the name
of our project, the version
, and the devDependencies
. This may seem weird at first for someone that hasn’t used Node or npm before, but you’ll see how npm is a very cool package manager for our project very soon.
You may be wondering what all of the grunt-contrib-****
packages do. Here’s a handy table of the popular packages.
Plugin | Description |
---|---|
contrib-jshint |
Validate files using JSHint |
contrib-uglify |
Minify files using UglifyJS |
contrib-watch |
Run tasks whenever watched files are changed |
contrib-clean |
Clean up files and folders |
contrib-copy |
Copy files and folders |
contrib-concat |
Combine files into a single file |
contrib-cssmin |
Compress CSS files |
contrib-less |
Compile LESS files to CSS |
contrib-imagemin |
Minify PNG, JPG, and GIFs |
contrib-compass |
Compile SASS to CSS using Compass |
contrib-htmlmin |
Minify HTML files |
For the full list of packages, visit the Grunt plugin repository.
Now that we have the packages we need defined, let’s install them.
With our package.json
file ready to go, go into your command line and type:
- npm install
You will see npm do its thing and pull those packages into a newly created node_modules
folder. Now we have these packages and are ready to use them in our project.
Now that our setup is all ready to go, let’s set up our tasks for Grunt to do!
To define our configuration for Grunt, we will use our Gruntfile.js
file. This is the default place where our settings will go.
In our Gruntfile.js
, let’s go ahead and add in the basic things we need for our project.
// our wrapper function (required by grunt and its plugins)
// all configuration goes inside this function
module.exports = function(grunt) {
// ===========================================================================
// CONFIGURE GRUNT ===========================================================
// ===========================================================================
grunt.initConfig({
// get the configuration info from package.json ----------------------------
// this way we can use things like name and version (pkg.name)
pkg: grunt.file.readJSON('package.json'),
// all of our configuration will go here
});
// ===========================================================================
// LOAD GRUNT PLUGINS ========================================================
// ===========================================================================
// we can only load these if they are in our package.json
// make sure you have run npm install so our app can find these
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-less');
grunt.loadNpmTasks('grunt-contrib-cssmin');
grunt.loadNpmTasks('grunt-contrib-watch');
};
We will use the module.exports
(wrapper) function. This is the Node way of exposing our configuration to the rest of our application. We don’t have to worry about this too much, but if you are interested, read this great article on the topic.
Inside of our grunt.initConfig()
, we have taken the information from our package.json
and saved it to pkg
. With this, we can use the attributes from our package.json file
. We can call the name of our project using pkg.name
and the version with pkg.version
. We could also expand this and even use an author.
Why do we need this? Good question. We’ll see the usage in a bit, but one of the cool things we can do is use these attributes to create comments at the top of our files with the project name, author, date built, and version! Pretty neat.
We have also loaded our Grunt plugins using grunt.loadNpmTasks()
. This is the way we can use the plugins that we brought in earlier using npm.
With our basic Grunt configuration ready, let’s look at configuring one of our packages. Let’s start with the JSHint package to lint our JavaScript files and tell us if there are any errors. This is the way for Grunt to know which files we want it to lint, minify, or anything else we want to do.
When we configure packages, it will go into our grunt.initConfig()
section and it will follow a certain structure. Here is the basic structure of configuring a Grunt package:
grunt.initConfig({
// configure jshint to validate js files -----------------------------------
jshint: {
options: {
reporter: require('jshint-stylish') // use jshint-stylish to make our errors look and read good
},
// when this task is run, lint the Gruntfile and all js files in src
build: ['Gruntfile.js', 'src/**/*.js']
}
});
This will be the basic format for how we configure our packages. We will:
jshint
)build
attribute and pass in files, directories, or anything else we want.When naming the tasks, we are going to name our main task build
. You can name this task what you want and you could even create more than one task. When you run Grunt, all of the tasks will automatically be run.
If you wanted to create tasks within the jshint
configuration, you could name them dev
and production
. Then we can call the tasks later by using jshint:dev
or jshint:production
.
Now that we have seen the basic setup for a Grunt package, let’s go ahead and start creating configurations for the tasks we want to do.
Here is the configuration for linting JavaScript files. It is the same as the example above. We are also bringing in the jshint-stylish
package to make our error output look good.
grunt.initConfig({
...
// configure jshint to validate js files -----------------------------------
jshint: {
options: {
reporter: require('jshint-stylish') // use jshint-stylish to make our errors look and read good
},
// when this task is run, lint the Gruntfile and all js files in src
build: ['Grunfile.js', 'src/**/*.js']
}
});
Go ahead and add in some JS into src/js/magic.js
:
var hello = 'look im grunting!'
var awesome = 'yes it is awesome!'
Now if we run in our command line:
- grunt jshint
We’ll see it lint the Gruntfile and all JS files inside the src
folder.
We can tell it to watch all JS files in our application, specific files, or all the files in a given folder using the double asterisks (**
) for all folders and single asterisk (*
) for all files. With linting out of the way, let’s look at minifying.
We’ll follow the same format. We will call the uglify
package, configure it, and tell it what files to use and create.
grunt.initConfig({
// get the configuration info from package.json ----------------------------
// this way we can use things like name and version (pkg.name)
pkg: grunt.file.readJSON('package.json'),
...
// configure uglify to minify js files -------------------------------------
uglify: {
options: {
banner: '/*\n <%= pkg.name %> <%= grunt.template.today("yyyy-mm-dd") %> \n*/\n'
},
build: {
files: {
'dist/js/magic.min.js': 'src/js/magic.js'
}
}
}
});
Here we are configuring an option called banner
. This will add a nice comment to the top of our minified file. Notice we are using the pkg.name
from the package.json file. In our build
, we are defining the file we want to create (dist/js/magic.min.js
) from the src file (src/js/magic.js
).
We will want more code in our file than just those two lines we made so let’s go grab a giant file to minify. Let’s go get jQuery.
http://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.js
Copy everything from the unminified jQuery file and paste it into our src/js/magic.js
file.
See how they have their cool comment at the top of the file? We can build one of those out using the banner option. With our file, let’s go ahead and minify it. Go into your console and type:
- grunt uglify
Now we have taken our file from the original 360kb to a nice sized 97.1kb!
We can also do much more than just minify a single file. We can take multiple files and minify them to one output file. This can speed up our sites since we are only serving one JS file to the users visiting our site. To configure multiple files is straightforward:
grunt.initConfig({
// get the configuration info from package.json ----------------------------
// this way we can use things like name and version (pkg.name)
pkg: grunt.file.readJSON('package.json'),
...
// configure uglify to minify js files -------------------------------------
uglify: {
options: {
banner: '/*\n <%= pkg.name %> <%= grunt.template.today("yyyy-mm-dd") %> \n*/\n'
},
build: {
files: {
'dist/js/magic.min.js': ['src/js/magic.js', 'src/js/magic2.js']
}
}
}
});
In this example, we are taking these two files and minifying them into magic.min.js
. We can also use the shortcut we learned earlier and just say all js files in the src
folder should be minified. You would use (src/**/*.js
) for that.
While we will be using LESS for this example, you can also use the grunt-contrib-compass
package to do the same for SASS. Here’s our configuration for compiling LESS files. Like our minifying JS example, we will use our config to define the source and output files.
grunt.initConfig({
...
// compile less stylesheets to css -----------------------------------------
less: {
build: {
files: {
'dist/css/pretty.css': 'src/css/pretty.less'
}
}
}
});
For this example, we aren’t going to be setting any options. Just compile our src/css/pretty.less
to a dist/css/pretty.css
file. Let’s add some LESS into our source file.
@red : #CC594A;
@yellow : #B8CC24;
@blue : #8BC5FF;
@purple : #6F3596;
body {
background:@red;
color:@yellow;
}
button {
background:@blue;
}
div {
background:@purple;
}
Now we have defined some LESS variables and applied them to our elements. Let’s go ahead and run our task!
- grunt less
Now we have created the dist/css/pretty.css
file all of our LESS compiled to CSS and that file now looks like a normal stylesheet.
body {
background: #cc594a;
color: #b8cc24;
}
button {
background: #8bc5ff;
}
div {
background: #6f3596;
}
Let’s look at two more things we can do with Grunt.
Like minifying JS files, this configuration will be straightforward. We get the drill now so let’s go ahead and create this configuration.
grunt.initConfig({
...
// configure cssmin to minify css files ------------------------------------
cssmin: {
options: {
banner: '/*\n <%= pkg.name %> <%= grunt.template.today("yyyy-mm-dd") %> \n*/\n'
},
build: {
files: {
'dist/css/style.min.css': 'src/css/style.css'
}
}
}
});
Now if you run:
- grunt cssmin
We will have our newly minified dist/css/style.min.css
file.
Now that we see how all of our tasks work, let’s make things more efficient and run everything with just one task. This is much better than running separate calls for grunt uglify
, grunt jshint
, and so on.
With Grunt, you can create tasks that will run multiple tasks at the same time. For example, let’s say we wanted to do all of our tasks above by just calling grunt
. When you run grunt
from the command line, Grunt will look for a task called default
. Let’s create that now so we can see what it looks like.
grunt.initConfig({
...
// ============= // CREATE TASKS ========== //
grunt.registerTask('default', ['jshint', 'uglify', 'cssmin', 'less']);
});
Now just run:
- grunt
And all of the tasks in our default
task will run!
Now let’s look at how we can register different tasks for different environments.
Let’s say we want to have Grunt work for us in a development environment and then when we go to production, we want different tasks to be run. We could define multiple tasks inside of each configuration. For example:
grunt.initConfig({
// get the configuration info from package.json ----------------------------
// this way we can use things like name and version (pkg.name)
pkg: grunt.file.readJSON('package.json'),
...
// configure uglify to minify js files -------------------------------------
uglify: {
options: {
banner: '/\n <%= pkg.name %> <%= grunt.template.today("yyyy-mm-dd") %> \n/\n'
},
dev: {
files: { 'dist/js/magic.min.js': ['src/js/magic.js', 'src/js/magic2.js'] }
},
production: {
files: { 'dist/js/magic.min.js': 'src/**/*.js' }
}
}
});
Now we can call them differently using a colon. Let’s create a task for development and production.
grunt.initConfig({
// ========= // CREATE TASKS =========
// this default task will go through all configuration (dev and production) in each task
grunt.registerTask('default', ['jshint', 'uglify', 'cssmin', 'less']);
// this task will only run the dev configuration
grunt.registerTask('dev', ['jshint:dev', 'uglify:dev', 'cssmin:dev', 'less:dev']);
// only run production configuration
grunt.registerTask('production', ['jshint:production', 'uglify:production', 'cssmin:production', 'less:production']);
});
Now we can call development by running:
- grunt dev
Or production by running:
- grunt production
Now we’ve seen how we can create multiple configurations for our tasks and call them differently.
Let’s look at the last thing we’ll be creating today. We’ll watch our files and have Grunt run every time a file changes!
The watch
task will run every time a file is changed and saved. All we have to do is configure it to watch certain files and tell it what to do when those files are changed.
We’re going to break from the basic build
configuration we’ve been using. Here we’ll separate it out into stylesheets and scripts. We do this because we want to use different tasks for each.
grunt.initConfig({
...
// configure watch to auto update ----------------
watch: {
// for stylesheets, watch css and less files
// only run less and cssmin stylesheets: {
files: ['src//*.css', 'src//*.less'],
tasks: ['less', 'cssmin'] },
// for scripts, run jshint and uglify
scripts: {
files: 'src/**/*.js', tasks: ['jshint', 'uglify']
}
}
});
Now we have configured watch to watch our stylesheets and scripts. In your console, run:
- grunt watch
Now we can see that Grunt will watch for changes and run the tasks it needs to.
We changed a JavaScript file and a CSS file. The respective tasks were run and we can go on developing!
This is a very powerful tool since we can do things like linting our files every time we save, compiling LESS, and we can even minify images.
Hopefully, this simplified look at Grunt will begin to grow some ideas about how to use Grunt for your specific workflow. Getting up and running is as easy as installing a package, configuring the package, and typing grunt
!
Definitely look through the official docs and the plugins list to get more ideas on how to use Grunt.
Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.
This textbox defaults to using Markdown to format your answer.
You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!