How to Quicken WordPress Development with Grunt JS

A couple of days ago Chris Coyier’s post regarding Grunt JS on the 24ways blog got me interested in playing with Grunt JS again. I’ve tried tinkering with Grunt but I never gotten it to work correctly for me. After reading Chris’s post and two more days of tinkering, I manage to come up with a configuration have helped hasten my wordpress development dramatically.

The configuration I’m going to talk about helps with 3 processes.

  1. Watches for changes on all PHP, Javascript and Sass files and reloads the browser automatically when anything changes (I can’t stress how useful this is during development).
  2. Processing all development scripts, concatenating and minifying them with one simple command.
  3. Detection of development and production servers, and automatically serves up the correct styles and javascript files.

I’m going to break up these three points and explain how to set these things up create the magic. This post might be a little heavy if you’re completely new to Grunt JS since I’m not going to explain everything. If so, it might be best for you if you check out Chris’s article before continuing. I’ll wait :)

Setting up Grunt JS with Compass

I prefer to work with Compass and Sass when it comes to CSS. In order to work with Compass. I’m going to first explain how to setup Grunt JS to work with Compass. If you have already gotten Compass to work, skip ahead to the next section where the meat is.

compass: {
  dev: {
    options: {
      require: 'susy', // optional if you don't use Susy. But you should!
      sassDir: 'dev/scss',
      cssDir: 'dev/css',
      fontsDir: 'dev/fonts',
      javascriptsDir: 'dev/js',
      imagesDir: 'dev/images',
      relativeAssets: true,

Watching and Reloading the browser for changes

One cool thing I really really like about Grunt JS is that it can reload the browser when any file changes. The tedious part is to figure out how to configure grunt to do that in the first place, which is what we’re going to tackle now.

To allow Grunt to reload your browser automatically when files changes, you have to setup a task in grunt.

Fire up the terminal in the folder where you stored your gruntfile.js and install the grunt-contrib-watch plugin.

$ npm install grunt-contrib-watch --save-dev

and load the plugin into your gruntfile.


This is an example of my watch function where I watched for changes in javascript files, scss files and php files. Adapt this to how you would like to structure your files and use Grunt’s globbing patterns to target your files.

watch: {
  scripts: {
    files: ['dev/js/*.js'],
    tasks: ['jshint', 'concat']
  compass: {
    files: ['dev/scss/{,*/}*.{scss,sass}'],
    tasks: ['compass:dev']
  php: {
    files: ['*.php', 'includes/{,*/}*.php'],
  options: {
    livereload: true,
    spawn: false

 * Quick and dirty explanation to Grunt. If you didn't catch on to the above two code snippets, read this. Otherwise, skip this part. 

In grunt, you configure tasks in this way. 

The first JSON object literal you see here (watch), refers to the task to be configured. 

The second object (scripts, compass and php) refers to targets. These can be named anyway you want them to, and can be trigged independently by running them with the :target suffix in terminal. (For example, grunt watch:compass will only watch for files within compass.)

The third object literal (files and tasks) are configurations that the plugin provides. Some defaults that grunt has are 'files', 'src', 'dest' for example. 

Options if placed as the second level (alongside scripts, compass and php) stands for global options, and will affect all other targets. IF they are placed within the second level objects like the example below, then they affect only the compass target. 

watch : { 
  compass: {
    options: 'xyz'

And I configured my default grunt task to watch the above mentioned files.

// Configuring the grunt task
grunt.registerTask('default', ['watch']);

There is just one thing left to have our browsers automatically reload whenever we save a file. We have to insert a javascript snippet that tells our browsers to reload. If you’re working with MAMP or any other server, the trick here is to make sure this livereload snippet is found at the root of your server.

In this case, I would create a file called livereload.js in the root folder with the following code.

document.write('<script src="http://' + ( || 'localhost').split(':')[0] + ':35729/livereload.js?snipver=1"></' + 'script>')

This allows the browser to fetch javascript codes that automatically refreshes the browser. The final step to get livereload working is to inject this livereload.js into your WordPress theme.

Within the functions.php file,

add_action( 'wp_enqueue_scripts', 'zell_load_dev_scripts' );
function zell_load_dev_scripts(){
  wp_enqueue_script( 'livereload', 'http://' . $_SERVER['SERVER_NAME'] . '/livereload.js', '', null, true );

We’re set and the browser will automatically reload when you save any compass file, javascript file in the dev folder or any php files.

Processing development scripts into production scripts

We are often told that we should minify and concatenate all styles and script files for production servers. But that work is INCREDIBLY time consuming. There’s a ton of wasted effort if your styles or scripts require changes (and there are always changes aren’t there :) ).

Grunt makes it super easy for front end developers to minify and concatenate all styles and javascripts with a simple command. The caveat is that you have to set it up first.

Here’s a sample config I have setup to concatenate javascripts.

concat: {
    dev: {
      src: [
        'dev/js/*.js', // All JS in the dev/js folder
      dest: 'dev/js/build/dev.js'

uglify: {
  target: {
    src: '<%= %>',
    dest: 'dist/js/production.min.js'

Concat(grunt-contrib-concat) and uglify(grunt-contrib-uglify) are two grunt plugins that I have used above. The configuration for Concat tells grunt to look out for all files in the dev/js folder, join them up together and spew them into a file called dev.js in dev/js/build while uglify is set to take the oupt from concat and convert it into production.min.js, located in the dist/js folder.

My configuration for minifying and concatenating these scripts are currently mediocre at best as I’m still focused on developing a website using Grunt. I’ll update this section as soon as I attempt to do more heavyduty converting work.

Automagic detection of local and production servers

One neat thing to add to the whole process is to allow our files to automatically differentiate development and production servers. One method of doing so is to look for a file called wp-local-config.php that is only present on my development server.

If this file is found, WordPress will enqueue development stylesheets and javascripts. Otherwise, it will enqueue the production stylesheets and javascripts.

// Checking if the wp-local-config.php file exists
$localConfig = $_SERVER['DOCUMENT_ROOT'] .'/wp-local-config.php';
if (file_exists($localConfig)) {
  // Load dev styles
  // load dev js
else {
  // Load Prod CSS
  // Load Prod JS

Here’s a sample on how to enqueue stylesheets and javascripts.

Enqueuing stylesheets
add_action( 'wp_enqueue_scripts', 'zell_load_dev_stylesheets' );
function zell_load_dev_stylesheets() {
  if( !is_admin() ) {
    wp_enqueue_style( 'zell_dev_stylesheet', get_stylesheet_directory_uri() . '/app/css/styles.css', array(), null );

Enqueuing javscripts
add_action( 'wp_enqueue_scripts', 'zell_load_dev_scripts' );
function zell_load_dev_scripts(){
  wp_enqueue_script( 'zell_dev_scripts', get_stylesheet_directory_uri() . '/app/js/build/dev.js', array( 'jquery' ), null, true );
  wp_enqueue_script( 'livereload', 'http://' . $_SERVER['SERVER_NAME'] . '/livereload.js', '', null, true );

With the presence of this script, WordPress will automatically detect the correct files to serve up for both production and development environments. One less job to do and more time to work on cool stuff!

Problems and thoughts

One problem I have yet to solve with the configuration I mentioned above was the order of concatenating javascript files. A possible way would be to create two folders, one to house all vendor related files while the other one holds all my javascript files.

This would work for small projects, but might become a headache for bigger projects if javascript files have to be arranged in a specific way. It would be great to hear your suggestions on this one.

Other than that, I think the configuration is good enough as it is for now. Take what I had above and try playing around. Let me know if this helped you or if you have any great suggestions!

  • Fabian Rios

    Thank you for a great tutorial!

    Im definitely gonna use this in my next wordpress project :)

    • Zell Liew

      Glad you like it

  • lester

    Thanks so much for sharing all your hard work and knowledge. You have written in a way that makes it easier to understand for a new-b like me. i’m so thankful for the dev community helping the little guys like me. you are wonderful! you helped me make my project better :)

    • Zell Liew

      Glad that you found this useful

      • lester

        i found it very useful ~ thanks again!
        quick question for you:
        what do you use for a boilerplate / bootstrap? i’m looking for complete, up-to-date head content for my wordpress local dev sites in the header.php file.
        any suggestions or favorites you like?
        thanks so much!

        • Zell Liew

          For WordPress , I stick to the Genesis Framework

          I do add some cleanups in addition to the Genesis Framework though.

          Its not an affiliate link so check it out all you want :)

          • lester

            cool! thanks so much.
            i assume you’re using child themes to do the cleanups . . . ?

          • Zell Liew

            yep thats correct

          • lester

            cool, thanks. what makes genesis work so well for you?
            do you ever use a theme like chris coyier’s blank_theme or another that’s completely stripped of most styling and functionality?
            i’m thinking i’d learn a lot from working more with existing themes but don’t want to overwhelm myself since php doesn’t come easy to me.
            i’m going to learn javascript soon, too, and think it will be much easier than php.

          • Zell Liew

            I started learning javascript before php, and javascript is far more intuitive to me as well, since I come from the frontend and visual side of the internet. Its great that you’re interested in the language.

            I initially wanted to create themes from scratch. However, someone told me about their worries if I were to create themes like that, because its likely not to be future proof.

            The choice of picking up Genesis was largely based on gut feel, and I just ran along with it. Turns out to be quite satisfactory indeed.

            Have you just started out with programming for WordPress? Why did you want to learn javascript?

          • lester

            hey zell,
            i think learning js first would probably have been a better idea than php first. from what i’ve learned just about everyone in the field needs to have a basic knowledge of php and a minimum of intermediate knowledge of js. i think js will be easy after my php experiences, and i am getting better with php after finding a good book (which was hard to find as well).
            i’ve gained a lot of structural design knowledge working from minimal themes like chris coyier’s blank theme. it’s definitely harder to learn in general, but it’s a rather ground up approach which i like. i just like learning the basic underlying structure of things.
            yes, i am fairly new to wordpress tinkering but getting deeper into it. js is more fun than php. it’s sort of like a cross between photoshop and php. you need some programming knowledge but it’s definitely more intuitive, and you can make pretty things and fun visual effects versus just looping through pages or stating conditional arguments (which is important, but frustrating a lot of times).
            i will check out genesis, although i don’t want to have to pay for anything right now. i’ve been learning a lot from the core wp themes.
            i’ve also worked a bit with child themes and think they are great. i think it’s good to have a well-rounded base of experience with all these different, but connected, ways of working. interesting chat ~ thanks for your comments and feedback.

          • Zell Liew

            I get what you mean by not wanting to pay. I didn’t want to pay at first, and I just progressed to a point where I thought it’ll be beneficial. No harm waiting at all, take your time.

            Great to hear that you’re having fun while learning. Thats the most important thing!

  • Banago

    Awesome article.

    Another issue that arises with this setup is assets produced by plugins. How would you go around to grab those too?

    • Zell Liew

      I’m getting a little confused here. Are you talking about creating plugins and the theme at the same time?

      • Banago

        Yes, I’m developing a site, for example, that I use the theme with multiple asset files and I’ve developed several plugins for the same site that have one css and js file each. Now I want to concatinate them too with the theme assets. What would you do in that scenario?

        • Zell Liew

          Mmm If I were in your scenario, I probably wouldn’t opt to concatenate all these files into one. I’ll keep these files separate and enqueue them with wordpress as necessary. Thats on the assumption if these plugins might be used for other projects as well.

          But if its solely for this project, and you are 100% sure that the theme won’t get deactivated and cause your plugins to fail, you might want to use the “mu-plugins” folder. In that case, you can write a globbing pattern that goes out of the current theme folder, into the mu-plugins folder and search for the js and css files respectively.

          I believe you can traverse up a level with “../”, and that might work to your advantage.

          • Banago

            Yeah, not an ideal setup to use Grunt with plugins. :)

          • Zell Liew

            We see eye to eye here :)

  • John Reid

    Thank you. I’ve read so many “how to” guides that tell me how great it is minifying scripts – but not a single one has distinguished between running your scripts on a separate development / production code base. I’m guessing they are amazing at debugging minified javascript but I haven’t reached that dizzying level.of skill yet so this is very useful.

    • Zell Liew

      Not sure how I missed this, but I’m glad it helped :)

  • Pingback: Config property watch.php.tasks missing – Can't get Grunt to watch PHP()

  • Pingback: Config property watch.php.tasks missing - Can't get Grunt to watch PHP - HelpDesk()

  • surewould85

    Thanks for putting this together. I’ve been attempting to follow your example and another ( but I keep hitting an error:

    “Verifying properties watch.php.files, watch.php.tasks exist in config…ERROR
    Unable to process task. Warning: Required config property “watch.php.tasks” missing.”

    I’m quite new to this and I thought I was following your example but I can’t seem to get the php to be watched appropriately. Do you have any idea what I could be doing wrong? I’ve also posed this to stackoverflow ( but haven’t been enlightened yet. Thanks again.

    • Zell Liew

      I’ve tried working with your code and it seems to run okay for me. What version of grunt are you on?

      • surewould85

        Thanks for the response. I recently installed grunt I believe, it says:
        grunt-cli v0.1.13
        grunt v0.4.5

        • Zell Liew

          That’s interesting. I’m on grunt 0.4.5 as well and I’m not getting the same error.

          Have you tried adding a task to php? Does that give you any more errors if you do so?

          • surewould85

            Whoops, looks like I had a rather old version of the watch defined in my package.json file.

          • Zell Liew

            Glad you got it sorted out :)

  • Luca Bottoni

    but if i change my css theme nothing is reloaded! you can paste all gruntfile?
    why you don’t insert the port on livereload.js????

    • Zell Liew

      The port doesn’t work when I tried it, which is why I’m not using it.

      Here’s the gist of my full grunt file right now. It has other items that are not mentioned in the article, so you might need to cherry pick a little:

  • Piet

    Thanks a lot for putting this together, very helpful for someone just starting to play with Grunt. Of course Chris’ article you refer to in the opening sentence is a must read too for Grunt beginners :)

    • Zell Liew

      Glad it helped :)

  • Zell Liew

    This is great! Thanks for sharing!