Grunt - Getting Started


By Ronnie Royston

Originally Published 5/12/2018
Updated 5/12/2018


Automation of tasks related to web development. Grunt, "The JavaScript Task Runner". In addition to file concatenation, linting, and minification, Grunt delivers a plugin that offers the equivalent of PHP's include. Static content such as page headers, navigation bars, or footers are automatically inserted across several HTML files, or web pages. Write header.html and footer.html in standalone files and Grunt automatically inserts them in all of your production .html files for you on demand. Without Grunt, change the footer on one page and you must then manually replicate, i.e. copy and paste, that update across all of your HTML files that contain that footer content.

Adopting and deploying Grunt is not trivial but this is a situation where one takes 1 step back to make leaps forward. Admittedly, I spent several hours reading and experimenting before getting Grunt running properly. The effort has already paid off. This article should enable you to get up and running with Grunt in minutes. Write once, paste everywhere, in milliseconds.


Developers use an Integrated development environment, IDE, rather than a simple text editor. Eclipse is a free favorite. I use Cloud9, an online IDE. Now install Grunt. There is no reason to regurgitate the installation steps which are already published by the Grunt team. Install Grunt now.

Grunt is modular in that the features are installed as plugins. Each plugin installs in a matter of seconds. Quick and easy. The example in this article utilizes grunt-processhtml, grunt-contrib-uglify, grunt-contrib-concat, grunt-contrib-htmlmin, and grunt-contrib-cssmin. Install these now.

The Process HTML Plugin

The processhtml plugin looks for specifically worded HTML comment sections and replaces them with the HTML snippets you specify. For example, a centralized footer snippet located at includes/footer.html gets inserted into any HTML file that includes the below code, at that location in the target document(s).

<!-- build:include includes/footer.html -->
<!-- /build -->

NOTE: the specified filepath in HTML files located in subfolders should specify reverse directory lookup, i.e. .., as in includes/footer.html. This is because the includes directory is one level up relative to subfolders in your source folder.

Folder Structure

The Grunt code below assumes a specific folder structure on your development machine / IDE. This code should be in Gruntfile.js in the root of your directory. Create a source, staging, and public folder in your root directory. Inside your source folder you have 4 folders - articles, css, includes, and js. Folder Structure Image All your .html files such as index.html, about.html, etc go here in the source folder. Your JavaScript goes in the js, CSS in the css flder, and finally I have an articles section on my webpage / webapp - thus the articles folder. You might have a different subfolder or set of subfolders, but this example shows how it's done.

Grunt Configuration

Starting out, one specifies each file that Grunt should touch as well as what operations Grunt should perform. This is spelled out in your Grunt file, or Gruntfile.js. Add a new HTML file, or webpage, and you must manually tell Grunt to process it by including it in your Gruntfile.js. This manual approach grows unwieldy as the number of HTML files, or webpages, your app has so automating Grunt with wildcards is key. Configured as below Grunt touches every HTML file in my source folder as well as my articles subfolder each time I run Grunt. The staging folder is a midway holding point that Grunt circles back to before performing its final task of dropping all files to the production public folder. Note the expand: true and src: ['*.html', 'articles/*.html', '!**/includes/**']. This is what tells Grunt to touch all HTML files.

Grunt Code

module.exports = function(grunt) {
        processhtml: {
            dist: {
                options: {
                    process: true,
            files: [{
                expand: true,
                cwd: 'source/',
                src: ['*.html', 'articles/*.html', '!**/includes/**'],
                dest: 'staging/',
                ext: '.html'
        uglify: {
            my_target: {             
                files: {
                    'staging/script.min.js': ['source/js/script.js']
        concat: {
            js: {
                src: ['source/js/firebase.js', 'source/js/firestore.js', 'source/js/mdc-web.min.js', 'staging/script.min.js'],
                dest: 'public/app.min.js',
        htmlmin: { // Task
            dist: { // Target
                options: { // Target options
                    removeComments: true,
                    collapseWhitespace: true
                files: [{
                    expand: true,
                    cwd: 'staging/',
                    src: ['**/*.html'],
                    dest: 'public/',
                    ext: '.html'
        cssmin: {
            target: {
                files: {
                    'public/app.min.css': ['source/css/mdc-web.min.css', 'source/css/style.css']
    grunt.registerTask('default', ['processhtml', 'uglify', 'concat', 'htmlmin', 'cssmin']);


There is a useful -- verbose switch that shows task details so use grunt -- verbose if you are having issues. One issue I encountered was using the % sign in one of my .html files since Grunt sees these as argument delimiters. The fix is to simply use the HTML character code.


Grunt is not a toy and it is raw. One missing letter or comma in a single file and it will fail and throw a funky error. However, the time savings of using the Grunt tool are well worth it. If you are a professional developer, automating mundane tasks is mandatory and Grunt is a widely used automation tool. I am very pleased with Grunt and will continue using it.

Automate a workflow or process today with standards based, library free JavaScript in the browser, in the cloud with NodeJS, or both. High Tekk can do that! See our services page for more details.

Dialog Title

Cell Phone Number

Your order ID is . The grand total is , or bitcoin.

This action cannot be undone. Continue?

Changes will be lost. Continue?

Delete location?

A signin link will be sent to your email address.

You privacy is top priority. Email addresses are never shared with a third party. For more information see our privacy policy.

Find a bug? Experience an error? How can we do better? We appreciate your feedback.

Delete order? This action cannot be undone.

Delete order? This action cannot be undone.

This action cannot be undone. Continue?