Using Grunt for WordPress Theme Development and Deployments

[Update 2013-05-15: I’m now using grunt-contrib-watch instead of grunt-regarde and grunt-livereload. It has a built in LiveReload server and is more streamlined. I updated the Gists, the tutorials, and my WordPress Starter Theme, Genesis Starter Child Theme, and Drupal 7 Starter Theme.]

There are plenty of options if you’re looking to use CSS preprocessors and JavaScript linting in your development workflow, but none as flexible and extensible as Grunt. In the past, I used CodeKit to handle all of this work, but Grunt has since taken over, giving me additional options such as deployments with greater control over the entire process. With one JavaScript file in the root of my WordPress theme, I can control Compass output paths, JSHint options, UglifyJS concatenation and minification, setup browser live reloading and multiple server deployments, allowing for easy expansion and adaption as things continue to change and grow.

Keep reading to find out how to use Grunt with your WordPress themes to speed up your development and allow you to deploy your code to your servers.

What is Grunt?

Grunt is an elegant task runner from Ben Alman written in Node. In it’s latest version 0.4, Grunt has become an elegant wrapper for any task you can imagine. There is a large community behind Grunt with many  contributed plugins that you can add to your projects. Whether you’re looking to create a full build process or just watch files for changes, Grunt has you covered.

I’m not going to get too in depth on how to install Grunt – check out their Getting Started guide for getting up and running on your system.

What Can Grunt Do?

There are countless tasks that Grunt can do for you, but in this case, we’re going to focus on those that will help our WordPress theme development: Compass preprocessing, JSHint to check out JavaScript, UglifyJS to concatenate and minify all JavaScript files, Watch to watch files for changes, trigger our tasks and LiveReload the browser, ImageMin to optimize all PNG and JPG images, and Rsync to deploy our files to remote servers.

All of this is accomplished by adding two files to your theme, a package.json to define which Grunt plugins to use as dependencies, and a Gruntfile.js to define our tasks and options. For more information on getting that all setup, my friend Jonathan Christopher has a great article about using Grunt with WordPress.

Using Grunt with WordPress Themes

Before we begin, I’d like to note that I have two WordPress starter themes setup that you use: one for use with most WordPress themes and one for use with the Genesis Framework. Both utilize all of the Grunt tasks we’re going to go talk about in this article, with plenty of theme functions and grids to get you started.

Let’s get going, shall we? Make sure Node and Grunt are installed on your system, and then create a package.json file in the root of your theme to define your dependencies.

[* Please note, package.json is subject to change. Check my Grunt WordPress and Drupal repositories on Github for the latest versions]

With that in your theme, you can then open the theme folder in your Terminal and run:

npm install

This will tell NPM to download all dependencies and put them in a node_modules folder. Now we’re ready to create our Gruntfile.js.

[* Please note, Gruntfile.js is subject to change. Check my Grunt WordPress and Drupal repositories on Github for the latest versions]

We first setup the livereload task, but give it no options (you can change the port here if you feel the need). [Update: This is now handled the Watch task.] You’ll need to install the LiveReload browser extension to connect, which you can get for Chrome, Firefox or Safari. Next, we give JSHint our options for how to lint our JavaScript and tell it which files to check in the all setting, followed by configuring Uglify to create source maps and concatenate and minify our files. In my setup, I have it automatically pull assets/js/source/plugins.js and all files inside assets/js/vendor other than Modernizr and put them into assets/js/plugins.min.js, as well as taking our main.js script and minifying.

Next, we tell Compass to use the settings in our external config.rb file for processing our SCSS. I chose to break config.rb out into a separate file instead of putting the options directly inside the Gruntfile.js in case I wanted to use the Compass ruby gem directly or use CodeKit, as both will work using the config.rb file.

The next section sets up Regarde for our watch task to monitor for changed files and triggering our tasks. The reason for using Regarde instead of Grunt Watch is that Watch doesn’t give access to changed files because it spawns tasks in subprocesses, which doesn’t play nice with LiveReload. It’s essentially a drop-in replacement, so swapping back to Watch won’t be an issue if the way it spawns processes changes. We tell Regarde to fire the compass and livereload tasks whenever an SCSS file is changed, and to fire jshint, uglify and livereload whenever a JS file is changed.

[Update: Watch now works with LiveReload and has it built in, so the above Regarde code is swapped out for Watch. It’s essentially the same code, we’re just adding an option to LiveReload the browser.]

The last task for this section is imagemin, which doesn’t automatically run as file are changed, but run manually with:

grunt imagemin

This will run OptiPNG and jpegtran on your images to optimize them before you deploy to save on bandwidth.

At the end of the Gruntfile.js, we load all our tasks and register our default task. To run Grunt, at the terminal, just enter


Now, let’s get right on the most fun part: using Grunt to deploy your theme to your servers.

Grunt running in iTerm2

Deploying Your WordPress Theme with Grunt

There are a number of deployment choices when it comes to Grunt tasks. You can use SSH to connect and run commands or transfer files over SFTP, you can use FTP to transfer files if you don’t have SSH access, or you can use Rsync to sync up your directories over an SSH connection. After trying all three, I settled on Rsync as it provided the best and quickest way to update my theme files on the servers I use most. I typically have SSH or SFTP access, so it’s not an issue for me, but if you have only have FTP access, it’s trivial to swap out the Rsync plugin with the FTP plugin for your deployments. I’ve used this method for client sites in the past and it works seemlessly.

In our Gruntfile.js, we have two servers setup: staging and production. Each sets the source as the root of our theme project locally and sets a destination and hostname for our remote server. Setting recursive tells rsync to traverse all subdirectories to make sure we’re getting all of our files, and syncDest will delete any files on the remote server that no longer exist in your local repository. I’m excluding a number of files as they aren’t needed for the theme to run on the server.

To trigger a deployment, at the Terminal, enter:

grunt rsync:staging


grunt rsync:production

And like that, you’ve now deployed your files to your server!

Wrap Up

I’ve changed over my deployments to using Grunt now instead of setting up Git post-commit hooks and bare repositories. Having the general framework in Grunt makes it quicker than configuring post-commit hooks on multiple servers, and it works just as well. With so many tasks available to extend and customize Grunt, it’s an invaluable tool for any development process.

This method can be easily tweaked to work with any development process, from Drupal themes to ExpressionEngine sites to static site builds and use in other systems. I love the flexibility of Grunt and plan to keep using and evolving my Gruntfile going forward. I hope you do the same!


  1. says

    Any recommendations for a more comprehensive walkthrough to get the grunt-contrib-livereload plugin up and running? After installing the chrome extension, copying you’re code verbatim, and reading the docs at the command line keeps telling me to put livereload-start task before any other task (which it is, so I’m not sure what the problem is).

    Perhaps I‘m not understanding how to integrate the chrome extension?

    Thanks for the article, other than livereload everything else has really helped out my workflow.

    • says

      I just updated the Gists associated with this post to the latest versions in my starter theme repos – try that and see if it works for you. That error usually pops up if you have anything before ‘livereload-start’ in grunt.registerTask, so make sure it’s the first task in your default list.

      • says

        So it ended up being two things…

        First, I was calling ‘grunt regarde’ instead of just ‘grunt’.

        Second, with the Chrome Extension – or whatever browser you’re using – you have to make sure that it has permission to access your file system by going to “Manage Extensions…” and selecting “Allow access to file URLs”.

        Spent way too long trying to debug the wrong things on that little problem :)

    • says

      Hey tomdp:
      If you aren’t too attached to firefox, consider using chrome.

      If you weren’t already aware, chrome has native support for sass debug symbols. (this was the feature that pulled me over from firefox to chrome for development)

      My only gripe is that livereload and the tools around it are mac-only, and doesn’t work on linux yet. Firefox has plugins for this task, but chrome stable can’t touch the filesystem yet.

      I’m waiting for source maps support in chrome stable. When that comes out, livereload won’t be neccessary any more.

  2. says

    Thanks very much for setting this up! I’ve been in the process of developing my own framework for client work for a while and your blank theme for WordPress combines a lot of the utilities I use. I tend to create a lot of responsive sites with the 12 Column responsive grid from bootstrap. I also use the font-awesome icon set. So in my own fork, I’ll be adding these features as well. In any case, it’s a nice clean framework.

  3. Simon says

    Hi Matt! Thank you so much for this awesome starter theme, it has helped me speed up my development radically. One question though: Is it possible to configure the Gruntfile so livereload for css happens without a page refresh, aka style injection? Now my page refreshes everytime I save. Thanks in advance!

    • says

      I actually just did this myself the end of last week – had to add a regarde task for watching CSS files only, not the Compass files to trigger the LiveReload task. The starter theme repos and the gists have been updated.

      I’m having trouble getting just style injection to work for JS files right now, so that’s still triggering a page refresh. If I take out the jshint and uglify tasks from regarde, it works for style injection, but having either or both of those in there throws a page refresh instead of style injection. I have an issue open on the regarde Github repo, so hopefully I can get that figured out soon!

    • says

      There are grunt tasks like grunt-contrib-connect that give you a server, and there is a PHP server, but I don’t use them because they’re not the ideal setup for me (and I haven’t ever tried to get them to work with MySQL). I use the built in Apache on my Mac with MySQL installed via Homebrew.

  4. Simon says

    I’m having trouble getting LiveReload to work, I’m getting various errors ‘Error: listen EADDRINUSE’ happening straight after when I run the grunt command. Research is telling me it could be port issues, which I’ve tried changing. It stops the errors, but LiveReload isn’t working..? Got me stumped…

  5. Tibor says

    Thanks for sharing this! First test deployment worked like a charm :-)

    I just noticed that the link to the Genesis Starter Theme seems to have a relative path, instead of pointing to the GitHub repo (wich also is awesome BTW).


  6. says

    Hi Matt,

    I did a lot of research on different kinds of starter themes, and yours was my favorite, so Thank you for all your work on this! The one issue I am running into is source maps not working the way I would expect them to. I expect source maps to basically allow for uglified javascripts to be minified, but in my expanded when trying to debug in my browser console if I have source maps enabled(so I can see which line number in which file is throwing an error, stepping through, etc.).

    This does not seem to be the case with this setup, and after poking around a bit I still couldn’t get it to work right. With my Sass files I can work around this by changing the output style from compressed to expanded, but the js is being handled by grunt which I am less familiar with. Any thoughts would be greatly appreciated.

  7. says

    Excellent post and walkthrough!

    I’m exploring using Grunt for my WordPress development and possibly deployment as well. But one question I have is in regards to deployment. Is there a task for rollbacks? So I have used Capistrano for deployments, but if I can streamline my workflow into using one tool for all, that would be great. But there are times when in working with the team that there’s a need to rollback a deployment.

    • says

      It’s just using rsync, so no way to do rollbacks with that. I’ve never really gotten into Capistrano since most of my clients are on cPanel type servers where that gets iffy. There may be a grunt task for that somewhere – check on their Github page.

      • says

        Right, I checked on the rsync repo and didn’t see anything. As rsync alone as a tool really isn’t meant for that sort of thing, I thought maybe there was another grunt task out there that would accomplish this sort of thing. Say take a snapshot of the directory prior to performing the rsync.

        But maybe that’s just an opportunity to explore and fill (if I only had the time haha).

        Capistrano is great, but as you say, some clients have their own hosting environments where that setup isn’t feasible.

        • says


          What you’re looking for is version control, like you mentioned Rsync isn’t really for that. You could set up a git repo for version control within your theme, that’d allow you to rollback to previous commits locally then just rsync them up to the server.


          • says


            Totally get what you are saying and I could do that. I’m already using git for a VCS. But I’d ideally like to be able to rollback directly on the server, rather than having to do all sorts of local manipulation of the repo.

            For example, in capistrano, you deploy with a simple command ‘cap deploy’ and then should the site blow up just type ‘cap deploy:rollback’ and that changes the symlink on the server back to the previous release. That’s ideally what I’m looking for here as well.

  8. dc says

    great article! How you you set a local folder for deployment, staging & production? Not really sure what these should be?

    dest: “~/path/to/theme”,
    host: “”

    • says

      Thanks, dc!

      The “dest” property is the path to the theme folder on your server (staging, production, etc). For my staging environment, it’s usually “~/clients/CLIENTNAME/wp-content/themes/THEMENAME”. The host is your SSH username and domain name to connect to.

  9. says

    This is (work) life changing for me.

    I am currently using git post-receive hooks to deploy, but from what you have outlined here there will be even less effort to deploy with grunt once I set this up.

    Thank you for the great write up.

  10. says

    Have you looked into using Grunt/Imagemin on the server to optimize images uploaded through the WordPress Admin? We’re looking into that for a site we are building, I know there are WordPress plugins, but wanted to explore this option as well.

    • says

      I prefer to do all that stuff locally prior to upload so I don’t hammer server resources. There are plugins like Smush It that will compress images uploaded through the media uploader, but I tend not to use them because of resource usage.


Leave a Reply

Your email address will not be published. Required fields are marked *