Using Templating Engines to Streamline WordPress Theme Development

Avatar of Charlie Walter
Charlie Walter on (Updated on )

The following is a guest post by Charlie Walter. Charlie has written for us before, when he’s not making me extremely envious. Now he’s back to share something completely different. We’re going to learn about methods he’s learned for streamlining WordPress theming using templating engines.

Templating engines are fantastic! They make complex programming languages much easier to write and include features that streamline the development process.

WordPress is a natural environment for us to see how templating engines work, not only because it’s template-driven, but because of its reliance on PHP. WordPress itself is lauded for its low barrier to entry for content publishers, but the knowledge of PHP that it requires for heavy customizing makes it a somewhat higher bar for developers wanting to get into the game beyond basic theming.

How Templating Engines Work

At its most basic, a templating engine is a package that is included in a project that allows us to compile one language into another.

I liken it to foreign language proficiency. Yes, you took four years of Spanish instruction in high school, and you might be able to read and write it on a decent level, but it’s still very hard to put a complex string of words together. If you had an app on your phone that could take your Spanglish and translate it into beautifully composed Spanish, that would be akin to what a templating engine does for PHP.

Let’s look at the basic example of the WordPress loop in vanilla PHP:

<?php if ( have_posts() ) : ?>
  <?php while ( have_posts() ) : the_post(); ?>
    <article>
      <h1><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></h1>
      <?php the_excerpt(); ?>
    </article>
  <?php endwhile; ?>
<?php else : ?>
  <p><?php _e( 'Sorry, no posts matched your criteria.' ) ?></p>
<?php endif; ?>

…and the same, but in Twig:

{% for post in posts %}
  <article>
    <h1>{{post.title}}</h1>
    {{ post.excerpt }}
  </article>
{% else %}
  <p>{{ __('Sorry, no posts matched your criteria.') }}</p>
{% endfor %}

If that doesn’t deserve a happy sliding penguin, then I don’t know what does.

Different Templating Examples for WordPress

I am going to run through several templating engines that can be used to make WordPress theme development that much more fun to write. In each case, we’ll give an overview of the templating engine itself, an example of how it can be used with the WordPress loop, then resources for plugging them into WordPress.


Jade-PHP

Jade-PHP makes it possible to write PHP in Jade files.

Wait! What’s Jade?

Jade is a clean, whitespace-sensitive syntax that is an HTML templating engine in its own right. So, for example, this …

.my-class
  a.btn(href="http://charliejwalter.net/") I'm a link

… compiles into this:

<div class="my-class">
  <a class="btn" href="http://charliejwalter.net/">I'm a link</a>
</div>

While Jade itself is an abstraction of HTML, Jade-PHP allows us to abstract PHP in alongside HTML making it an ideal fit for WordPress theme development.

The Setup

WordPress templates are written in and saved inside .jade files. Using Grunt and the grunt-jade-php task, we can compile Jade files (.jade) into the PHP files (.php) WordPress requires to do its thing.

The Jade templates are a one-to-one relationship with WordPress templates. In other words, where WordPress has a page.php file for the default page template, we would include a page.jade file in our directory that our Grunt task will translate into the PHP file. WordPress will then use the compiled PHP files as if we had written the PHP inside of it, though we never had to touch PHP at all. Pretty rad!

Note: These PHP files need to be where you usually put them.

One downside is we have to compile our Jade to PHP locally and, when that happens, Jade-PHP churns out a full HTML document for each template rather than using WordPress methods like get_header() and get_template_part() to re-use templates. This isn’t a problem for maintenance, but in terms of performance, we would be loading code that could have been cached.

The way I structure my files personally is to create both source and compiled folders in my project to keep all the source files separate from the compiled versions. The compiled folder is what WordPress will actually use as the theme. However, files that don’t need compiling will need to be copied into this compiled folder. This can be done with grunt-contrib-copy. It also means we can watch these files and hook into livereload to refresh our work on the fly!

├── source
	├── header.jade
	├── index.jade
	├── footer.jade
	├── functions.php
	├── style.css
├── compiled
	├── header.php
	├── index.php
	├── footer.php
	├── functions.php
	├── style.css
├── Gruntfile.coffee
└── package.json

WordPress will try to read the source folder as another theme, which will prompt an error message along the lines of “Template is missing” since it cannot locate all the files needed to construct a complete theme. Not ideal, though you could theoretically exclude the source files in the deployment process to prevent that sort of message from popping up.

One thing to note about Jade-PHP is the lack of specific syntax highlighting for Atom, Sublime or TextMate, at least at the time of this writing. That could be a deal-breaker for some.

The WordPress Loop in Jade-PHP

:php
  if (have_posts()) :
    while (have_posts()) : the_post();

      article
        h1: a(href= the_permalink())= the_title()
        != the_excerpt()

    endwhile;

Jade-PHP Starter Theme

Download a Jade-PHP starter theme for WordPress.

Jade

I always find building WordPress themes terribly messy because of how tough it is to make PHP and HTML look good together in the same markup. We can use Jade on its own to help with the lifting. The primary difference here from Jade-PHP is that our assets will be compiled server-side. That means there is no local compiling needed because it will happen on the fly as the templates are accessed. Plus, that solves the issue of templates compiling into full HTML documents, allowing us to take advantage of Jade’s more advanced templating features, such as extending one template into another.

The Setup

First off, we need the Jade compiler for PHP, which can be found in the Jade repository on GitHub under the “Implementations in other languages” section.

Then, let’s create the composer.json file in our theme folder:

{
  "require": {
    "path-to-project/jade-php": "*"
  }
}

Now, run the Composer installation. If your Composer installation is global that would be:

composer install

Or, If composer is installed locally in the project:

php bin/composer install

Now, we need the following files in our theme directory:

  • compiler.php
  • page-data.php
  • layout.jade
  • index.jade
  • functions.php
  • index.php

Note: All WordPress themes require style.css as well, but we’re leaving it out of this and other lists because it has no bearing on getting started with our templating engines.

compiler.php

This file takes care of returning the Jade compiler into your templates by requiring it everywhere.

<?php
  require_once('vendor/autoload.php');
  use Jade\Jade;
  return new Jade();
?>
page-data.php

This is for common page variables we want to make available for reuse.

<?php

$data = array(
  'wp_title' => wp_title('', false),
  'wp_head' => output_buffer_contents(wp_head),
  'wp_footer' => output_buffer_contents(wp_footer),

  'template_directory_uri' => get_template_directory_uri(),
  'stylesheet_url' => get_bloginfo('stylesheet_url'),
  'home_url' => esc_url( home_url( '/' ) ),
  'blog_title' => get_bloginfo(),

  'pages' => get_pages(),
  'categories' => get_categories('show_count=0&title_li=&hide_empty=0&exclude=1')
);

// To compensate for WordPress not providing a url for each post
foreach ( $data['pages'] as $page ) {
  $page->permalink = get_permalink($page->ID);
}

// To compensate for WordPress not providing a url for each category
foreach ( $data['categories'] as $category ) {
  $category->link = get_category_link( $category->term_id );
}

return $data;

?>
layout.jade

Extending templates into others is a huge benefit to Jade. This allows us to create a shell that can be used as the primary building block for our site layout, then modify it as it’s extended.

This could could be just about anything, but could look something like this:

doctype
html(lang='en')

head
  meta(charset="UTF-8")
  title= wp_title('')
  link(rel="stylesheet" type="text/css" href= bloginfo('stylesheet_url') media="screen")
  - wp_head();

body

  header
    a(href= esc_url( home_url( '/' ) ))= get_bloginfo()
    - get_template_part('nav')

  block content

  :php

    get_sidebar();
    wp_footer();
functions.php

Most WordPress themes make use of a functions.php file. In this context, we are adding a function to it to compensate for the lack of a get_wp_head() method:

<?php

function output_buffer_contents($function, $args = array()){
  ob_start();
  $function($args);
  $contents = ob_get_contents();
  ob_end_clean();
  return $contents;
}

?>
index.php

This is the primary template or homepage in most WordPress theme directories. Here is a working example of index.php that includes the Jade compiler and page data:

$compiler = include('compiler.php');
$data = include('page-data.php');

$data['posts'] = array();

if ( have_posts() ) :

  while ( have_posts() ) : the_post();

    array_push($data['posts'], array(
      'permalink' => get_permalink(),
      'title' => get_the_title(),
      'excerpt' => get_the_excerpt()
    ));

  endwhile;

  $data['next_posts_link'] = get_next_posts_link('Older');
  $data['previous_posts_link'] = get_previous_posts_link('Newer');

endif;

echo $compiler->render('index', $data);

Here, the familiar WordPress loop is used but, rather than rendering the markup within the loop, we add properties to a posts array on the page $data where we want Jade to compile and populate the content.

The WordPress Loop in Jade

Here’s the WordPress loop, in Jade:

each post in posts
  article
    h1
      a(href= post['permalink'])= post['title']
    != post['excerpt']

Jade Starter Theme

Download a Jade starter theme for WordPress.

Mustache

I mustache you a question: do you want less logic in your templates? Mustache offers a logic-less approach to templating, which forces you to keep the logic separate from the templates, which effectively declutters them. Bonza!

The Setup

The set up is very similar to how we render the Jade templates. in that the compiler for it can be installed using Composer.

First off, let’s create the composer.json file in our theme directory:

{
  "require": {
    "mustache/mustache": "~2.5"
  }
}

Run the Composer install and, just like that, we can add the following to our WordPress page templates to ensure the compiler runs:

echo $mustache->render('index', $data);

Now that we’re armed with Mustache, we need to add a mustache-compiler.php file to our theme folder in order to require the compiler to load in our templates, and to do so without needing to specify an explicit path to the views folder where our sources file will reside:

<?php

require_once('vendor/autoload.php');

return new Mustache_Engine(array(
   'loader' => new Mustache_Loader_FilesystemLoader(get_template_directory() . '/views')
));

?>

The WordPress Loop in Mustache

{{#posts}}
<h1><a href="{{permalink}}">{{{title}}}</a></h1>
{{{excerpt}}}
{{/posts}}

Mustache Starter Theme

Download a Mustache starter theme for WordPress.

Timber

So, you like the ideas shared in this post so far but might not want to go through all the setup. This is where Timber comes in.

Timber is available as a free WordPress plugin, making it perhaps the easiest to install of the templating engines we’ve covered here. In fact, Timber itself isn’t the templating engine, but rather relies on Twig for that while acting as the configuration needed to write Twig files. Plus, it boasts a library that integrates WordPress hooks into Twig making it every bit as powerful as writing native PHP in WordPress.

Oh yeah, and we have an article that covers the ins and outs for getting started with Timber.

The Setup

Download and install the plugin right from the WordPress Plugins repository.

Next up, we’ll need to make sure our theme directory includes the following files:

  • layout.twig
  • index.twig
  • functions.php
  • index.php
layout.twig

This will be the primary chunk of markup for our site that includes the document <head> and the basic layout of the site. In other words, we can extend it on all of our Twig template files and change out certain blocks on a template-by-template basis.

Here’s a basic example using a pretty common site layout:

<!DOCTYPE html>
<html lang='en'>
<head>
  <meta charset="UTF-8">
  <title>{{wp_title}}</title>
  <link rel="stylesheet" type="text/css" href="{{theme.link}}/style.css" media="screen">
  {{wp_head}}
</head>
<body>

<header>
  {{site.name}}
</header>

{% block content %}
{% endblock %}

{{wp_footer}}

</body>
</html>

Anything wrapped in double braces {{ }} are variables that Timber will fill in with the compiled output from Twig. And, when we extend this template into other templates, we will be able to modify each variable directly in the template where it is extended.

functions.php

This is the same standard file that comes packaged with most WordPress themes. What we’re adding to it are instructions for how Timber is configured, as well as definitions for any variables we call in our templates.

<?php

add_filter('timber_context', 'add_to_context');
function add_to_context($data){

  $data['menu'] = new TimberMenu();
  $data['categories'] = Timber::get_terms('category', 'show_count=0&title_li=&hide_empty=0&exclude=1');

  return $data;
}

?>
index.php

This is often the primary or homepage template of a WordPress theme. We need to call the variables we defined in functions.php so the template knows what to render when Timber compiles the Twig files to PHP. In this case:

<?php
  $context = Timber::get_context();
  $context['pagination'] = Timber::get_pagination();
  Timber::render('index.twig', $context);
?>

The WordPress Loop in Timber

{% for post in posts %}
  <article>
    <h1>{{post.title}}</h1>
    {{post.get_preview}}
  </article>
{% endfor %}

Timber Starter Theme

Download a Timber + Twig starter theme for WordPress.

Timber + Jade (…Twade?)

While Timber includes one templating engine out of the box, we can configure it to use something else in addition to Twig. It might seem silly to use two templating engines together but, if you like the features that Twig offers and prefer writing code in Jade…well, we can do that!

The Setup

We’ll be using the standard Timber configuration but compile Jade locally into Twig. People compile Jade into another other syntaxes with AngularJS all the time so why not do it Timber as well?

Here’s how our theme folder will be structured:

├── source
	├── views
		├── header.twade
		├── index.twade
	├── footer.twade
	├── index.php
	├── functions.php
	├── style.styl
├── compiled
	├── views
		├── header.twig
		├── index.twig
	├── footer.twig
	├── index.php
	├── functions.php
	├── style.css
├── Gruntfile.coffee
└── package.json

Once again, WordPress will attempt to load the source folder as its own theme unless we exclude it during deployment.

See that funny .twade extension? Those are just Jade files that get compiled (locally with Grunt) into .twig files that—you guessed it—are read by the .php files and compiled into PHP server-side.

The WordPress Loop in Timber + Jade

{% for post in posts %}
h1
  a(href="{{post.permalink}}") {{post.title}}
{{post.get_preview}}
{% endfor %}

Timber + Jade Starter Theme

Download a Timber + Jade starter theme for WordPress.

Shout out for Timber + Jade, minus the Twig

Alternative to the above, we can bypass Twig altogether to marry Jade’s templating features with the simplicity of Timber’s setup and feature-rich library. This is exactly the same setup as Timber, but intercepting Timber::render with this in our index.php instead:

<?php
  $compiler = include 'compiler.php';
  $context = Timber::get_context();
  $context['pagination'] = Timber::get_pagination();
  echo $compiler->render(get_template_directory() . '/views/index.jade', $context);
?>

That gives us the ability to write the WordPress loop as:

each post in posts
  h1
    a(href= post->permalink)!= post->title
  !=post->get_preview

Here is a starter theme for a pure Timber + Jade one-two punch.

Wrapping up

Templating engines are fantastic and work great with WordPress. It is purely a matter of preference as far as which one works best for your skill set and the website you are building.

Sites that require lots of complex views may benefit from an engine that offers more logic in the templates like Timber or Jade. Seasoned PHP developers might prefer to Mustache for its ability to keep all the logic on the server.

I’ve set up starter themes for all of the options written in this post. Please feel free to use them to get the ball rolling on your own projects, or just to experiment:

Happy theming!