Develop WordPress Themes Faster with Gulp

By Craig Buckler
We teamed up with SiteGround
To bring you the latest from the web and tried-and-true hosting, recommended for designers and developers. SitePoint Readers Get Up To 65% OFF Now

faster development

This article is part of a series created in partnership with SiteGround. Thank you for supporting the partners who make SitePoint possible.

WordPress’ simple theme development is partly responsible for its success. A developer with front-end and PHP knowledge can consult the excellent Codex and get started on their next masterpiece.

Theme development is possible with just a text editor and graphics package, but modern toolsets can revolutionize your workflow. In this tutorial we’ll use Gulp to run tasks including:

  • copying newer PHP theme files
  • optimizing images
  • compiling Sass SCSS files into a single, minified CSS file
  • merging ordered JavaScript files, remove debugging statements and minifying
  • automatically refreshing the browser when files are updated.

What is Gulp?

Gulp is a JavaScript-based build system which takes your source files and transforms them into optimized versions. If you’re new to Gulp, please refer to An Introduction to Gulp.js for full installation and usage instructions. A summary of the initial steps:

  1. Install Node.js.
  2. Install Gulp globally: npm install gulp-cli -g
  3. Create a project folder and navigate into it: mkdir mytheme followed by cd mytheme.
  4. Initialize your project with npm: npm init

Your Project Files

A Gulp (or any) build process requires a set of original source files containing your unmodified code and images. These are processed, manipulated and minified to create build files.

WordPress is installed to a web server folder, perhaps /var/www/ on a Linux/Apache installation. Your WordPress theme must be defined in its own sub-folder within /wp-content/themes/. Therefore, the folder containing our built files could be /var/www/wp-content/themes/mytheme/. At a minimum, themes require two files:

  • a style.css stylesheet containing meta-data in comments at the top, and
  • an index.php template file.

Most themes contain many more files for presenting posts, pages, indexes, categories, tags, and errors. Cross-page partials such as headers and footers are usually defined as are image and JavaScript files.

You could place your source files somewhere within the mytheme folder. That may be useful if you’re distributing a theme to be downloaded, modified and built by others. However, for the purpose of this tutorial, we’re going to use a source folder which is inaccessible from the web server, e.g. ~/mytheme/. The benefits of this approach:

  1. Your theme source files can be managed in a single folder and repository without polluting the build or WordPress folders.
  2. The final built theme folder contains only the files it requires.
  3. Gulp, its plug-ins and other applications are not contained in the theme folder. They cannot be accidentally copied to a production server which is unnecessary and could have security implications.

The source project folder requires a further four sub-folders:

  • template – the WordPress PHP theme files
  • images – images used by your theme
  • scss – Sass SCSS source files
  • js – any number of separate client-side JavaScript source files.

Install Dependencies

From the source folder (~/mytheme/), run the following npm command to install Gulp and all plug-ins as development dependencies:

npm install --save-dev autoprefixer browser-sync css-mqpacker cssnano gulp gulp-concat gulp-deporder gulp-imagemin gulp-newer gulp-postcss gulp-sass gulp-strip-debug gulp-uglify gulp-util postcss-assets

A node_modules folder will be created which contains the module code. That should be omitted from your source control system (for Git users, add node_modules to your .gitignore file).

Create a Gulp Configuration File

Create a new gulpfile.js configuration file in the root of your source folder. Add this code to get started:

// Gulp.js configuration
'use strict';

const

  // source and build folders
  dir = {
    src         : 'src/',
    build       : '/var/www/wp-content/themes/mytheme/'
  },

  // Gulp and plugins
  gulp          = require('gulp'),
  gutil         = require('gulp-util'),
  newer         = require('gulp-newer'),
  imagemin      = require('gulp-imagemin'),
  sass          = require('gulp-sass'),
  postcss       = require('gulp-postcss'),
  deporder      = require('gulp-deporder'),
  concat        = require('gulp-concat'),
  stripdebug    = require('gulp-strip-debug'),
  uglify        = require('gulp-uglify')
;

// Browser-sync
var browsersync = false;


// PHP settings
const php = {
  src           : dir.src + 'template/**/*.php',
  build         : dir.build
};

// copy PHP files
gulp.task('php', () => {
  return gulp.src(php.src)
    .pipe(newer(php.build))
    .pipe(gulp.dest(php.build));
});

We’re defining our default folders, loading modules, then creating a php task to copy new and updated files to the theme folder. The task has been kept intentionally simple to copy the PHP source files as-is.

Save gulpfile.js and create a few .php files in the source template folder. Then enter the following command:

gulp php

All the files will be copied to the theme folder (/var/www/wp-content/themes/mytheme/).

Image Processing

Image files can often be compressed further using tools such as imagemin. Add the following code to gulpfile.js:

// image settings
const images = {
  src         : dir.src + 'images/**/*',
  build       : dir.build + 'images/'
};

// image processing
gulp.task('images', () => {
  return gulp.src(images.src)
    .pipe(newer(images.build))
    .pipe(imagemin())
    .pipe(gulp.dest(images.build));
});

Save then run gulp images. Compressed versions of any new or updated images in the source images folder are copied to /var/www/wp-content/themes/mytheme/images/.

Sass Compilation

WordPress cannot use Sass files directly; you must compile to a single style.css file. Add the following code to gulpfile.js:

// CSS settings
var css = {
  src         : dir.src + 'scss/style.scss',
  watch       : dir.src + 'scss/**/*',
  build       : dir.build,
  sassOpts: {
    outputStyle     : 'nested',
    imagePath       : images.build,
    precision       : 3,
    errLogToConsole : true
  },
  processors: [
    require('postcss-assets')({
      loadPaths: ['images/'],
      basePath: dir.build,
      baseUrl: '/wp-content/themes/wptheme/'
    }),
    require('autoprefixer')({
      browsers: ['last 2 versions', '> 2%']
    }),
    require('css-mqpacker'),
    require('cssnano')
  ]
};

// CSS processing
gulp.task('css', ['images'], () => {
  return gulp.src(css.src)
    .pipe(sass(css.sassOpts))
    .pipe(postcss(css.processors))
    .pipe(gulp.dest(css.build))
    .pipe(browsersync ? browsersync.reload({ stream: true }) : gutil.noop());
});

Launch this new task with gulp css to:

  • run the Gulp images task first (images may be required in your CSS)
  • compile the Sass code in the source scss/style.scss file using the fast LibSass compiler
  • use PostCSS to automatically add asset references, apply vendor prefixes, pack media queries together, and minify the resulting CSS code
  • output the stylesheet to /var/www/wp-content/themes/mytheme/style.css.
  • force a Browsersync CSS reload (more about that later).

The source scss/style.scss file must include the WordPress theme meta data at the top, e.g.

/*!
    Theme Name: My Theme
    Theme URI: http://www.sitepoint.com/
    Description: Demonstration theme
    Version: 1.0.0
    Author: Craig Buckler (@craigbuckler)
    Author URI: http://www.sitepoint.com/
    Tags: Gulp

    License: MIT
    License URI: http://opensource.org/licenses/mit-license.php
*/

@import '_base';
@import '_forms';
@import '_tables';
@import 'components/_widget1';
// etc...

It is important to use /*! as the first line. This ensures the cssnano minifier does not remove the comment and render your theme unusable.

The postcss-assets plugin allows you to refer to image assets using code such as:

.widget1 {
  width: width('myimage.jpg');
  height: height('myimage.jpg');
  background-image: resolve('myimage.jpg');
}

You can also inline images with automatic Base64 encoding:

.widget2 {
  background-image: inline('myimage.jpg');
}

JavaScript Processing

Add the following code to gulpfile.js:

// JavaScript settings
const js = {
  src         : dir.src + 'js/**/*',
  build       : dir.build + 'js/',
  filename    : 'scripts.js'
};

// JavaScript processing
gulp.task('js', () => {

  return gulp.src(js.src)
    .pipe(deporder())
    .pipe(concat(js.filename))
    .pipe(stripdebug())
    .pipe(uglify())
    .pipe(gulp.dest(js.build))
    .pipe(browsersync ? browsersync.reload({ stream: true }) : gutil.noop());

});

Run this new task with gulp js to:

  • process all JavaScript files in the source js folder
  • order the files appropriately. Add comments at the top of your JavaScript files to declare dependencies, e.g. // requires: lib1.js or // requires: config.js lib1.js.
  • concatenate into a single file
  • strip all debugging and console logging statements
  • minify the code
  • output the resulting code to /var/www/wp-content/themes/mytheme/js/scripts.js.
  • force a Browsersync CSS reload (more about that later).

Run Everything

Rather than calling each task separately, we can add the following code to gulpfile.js:

// run all tasks
gulp.task('build', ['php', 'css', 'js']);

You can now use gulp build to run the php, js, css and images tasks in parallel. (Note images is a dependency of the css task so we need not call it directly.)

Enable File Watching and Browsersync

Your workflow can be radically improved by:

  1. Letting Gulp watch for file changes before launching the appropriate task.
  2. Automatically reloading CSS and JavaScript files when they change (without a page refresh).
  3. Automatically refreshing the page when a template file changes.

First, we need to define a browsersync task in gulpfile.js. This will create a proxy server to your web server running WordPress on localhost (change this domain or use an IP address as necessary):

// Browsersync options
const syncOpts = {
  proxy       : 'localhost',
  files       : dir.build + '**/*',
  open        : false,
  notify      : false,
  ghostMode   : false,
  ui: {
    port: 8001
  }
};


// browser-sync
gulp.task('browsersync', () => {
  if (browsersync === false) {
    browsersync = require('browser-sync').create();
    browsersync.init(syncOpts);
  }
});

Now add a watch task to run Browsersync, watch for file changes and run the appropriate task:

// watch for file changes
gulp.task('watch', ['browsersync'], () => {

  // page changes
  gulp.watch(php.src, ['php'], browsersync ? browsersync.reload : {});

  // image changes
  gulp.watch(images.src, ['images']);

    // CSS changes
  gulp.watch(css.watch, ['css']);

  // JavaScript main changes
  gulp.watch(js.src, ['js']);

});

Finally, add a default Gulp task which runs an initial build and starts the watch task:

// default task
gulp.task('default', ['build', 'watch']);

Now run gulp from the command line. The console will display output which includes lines similar to:

[BS] Proxying: http://localhost
[BS] Access URLs:
 -------------------------------------
       Local: http://localhost:3000
    External: http://192.168.1.99:3000
 -------------------------------------
          UI: http://localhost:8001
 UI External: http://192.168.1.99:8001
 -------------------------------------
[BS] Watching files...

Rather than loading your development site from http://localhost/, enter the address http://localhost:3000/ or the External URL if you are viewing from another device. Your WordPress site will load as before but Gulp will watch for changes and apply the updates immediately. You’ll need never switch to your browser and click refresh again!

Hit Ctrl/Cmd + C when you want to stop Gulp processing.

Enhance Further

We’ve covered the basics of WordPress theme development with Gulp but there are several thousand plug-ins to aid your workflow. You could consider additional tasks to:

A few hours writing Gulp tasks could save many days of manual processing over the long term.

We teamed up with SiteGround
To bring you the latest from the web and tried-and-true hosting, recommended for designers and developers. SitePoint Readers Get Up To 65% OFF Now
  • OzzyLovesBabyShampoo

    Right away, the source folder instructions are confusing. Where do I put my source folder? In wp-content? In themes? Apparently, it’s not supposed to go in the ‘mytheme’ directory.

    First you say the source folder is /~mytheme/ in two different places:

    1.) “…we’re going to use a source folder which is inaccessible from the web server, e.g. ~/mytheme/

    2.) “From the source folder (~/mytheme/), run the following npm command…”

    Then, the very first thing you do in your Gulpfile.js is define the source folder as src/ rather than ~/mytheme/:

    // source and build folders
    dir = {
    src : ‘src/’,
    build : ‘/var/www/wp-content/themes/mytheme/’
    },

    So, what is supposed to be the name of the source folder and where am I supposed to put it?

    • Craig Buckler

      Thanks Ozzy.

      A few clarifications.

      1. Source folder
      The article advises you not to have the Gulp source files in your theme folder and lists three benefits.

      2. BrowserSync
      Setting open: true means BrowserSync will automatically launch your browser. That’s fine, but only if you’re running WordPress on your local PC. That will fail if you’re using a virtual machine or another server.

      • OzzyLovesBabyShampoo

        As for point 1, I originally asked where the source folder goes since your article does not make it clear, but after 2 days with no answer, I edited my response and just provided the answer for anyone else who was confused.

        In your article, first you call the source folder ‘~/mytheme/’ and then in your gulpfile.js you call it ‘src/’ so… yeah, confusing:

        (1.) “for the purpose of this tutorial, we’re going to use a source folder which is inaccessible from the web server, e.g. ~/mytheme/”

        (2.) “From the source folder (~/mytheme/), run the following npm command to install Gulp and all plug-ins as development dependencies”

        (3.) The source folder in the gulpfile.js code is not ‘~/mytheme/’ it is ‘src/’:

        // source and build folders
        dir = {
        src : ‘src/’,
        build : ‘/var/www/wp-content/themes/mytheme/’
        },

        And you never said where that source folder should go in order reap the benefits you’re talking about, so how a different source path should be set up is not clear. The source folder has to go somewhere and keeping it in the theme file makes the project tidy and I don’t know where else to put it since you didn’t mention it. Troubleshooting broken paths could take forever.

        • Sp4cecat

          That would make *~/mytheme/src* the place that gulp looks for its source files. For clarity, it should have perhaps said “we’re going to have to use a *development* folder ..” I didn’t read the article, btw, just perused the gulpfiles to clarify myself on a few things – I’m moving over to Webpack :)
          In Linux or Unix (which is what OSX uses) prefixing a path with ~/ indicates a home directory.
          Anyway, the point is that you want Gulp to build the nice theme stuff for you as you change the source files; if you’re using a versioning system you want to record the source files (ie .jsx, .scss etc) and not the actual .css and packed-up .js files that they create and copy to the theme directory. How you reconcile that with your versioned directories is up to you I guess.
          Anyway, the article pre-supposes that the reader is up with certain best-practice conventions; if you’re coming to it from old-school practices yeah, you’re going to get a little confused.

          • OzzyLovesBabyShampoo

            I’ve been using Gulp for 2 weeks with a static site, Sass, and all the same Gulp plugins in the article (mostly), so I know what it does. The file paths just needed to be explained properly if I’m supposed to know where to put my src folder. I just put it in the theme folder for the reasons I explained in my first post. I’ll put ‘src’ on my .gitignore file if I don’t want to share it. I’m not really worried about it. And working direct to remote locations seems like a pretty terrible work, so not sure why that’s the default example (I guess?) in the article.

            Next, I will be using Roots/Sage, which uses Gulp. I will probably learn Webpack after that just because any place I plan to work will probably be using it, but I think Roots/Sage is probably the best WordPress flow based on what I’ve read.

  • OzzyLovesBabyShampoo

    Your gulp task doesn’t work for Browser Sync at all with MAMP.

    EDIT: The reason is because your example code has ‘open’ set to ‘false’ for some inexplicable reason. Set it to true and Browsersync works.

  • OzzyLovesBabyShampoo

    btw, here’s something odd: my set-up is working perfectly fine without a package.json file anywhere in site. How is that even possible?

    I recreated this so many times that I don’t remember if I skipped the step or accidentally deleted the file, but it’s been working fine for the last 4 days. I’ve even shut down the terminal and put the computer to sleep several times and when I start back up the ‘gulp’ process, it continues to work no problem and read the gulpfile.js no problem. I’m not sure how it knows where the entry point is since there’s no package.json file to even tell it the entry point is gulpfile.js.