I’ve spent a good portion of my professional career working with WordPress, and I’ve come to learn a couple of things: Popularity breeds contempt, and to dismiss something based solely on the critiques of others is a missed opportunity.
WordPress and I started out great, but as time went on we became more and more distant. It’s not that we were spending less time together, but it felt like we were just going through the motions. The spark was fading, yet I still knew it was a very important part of what I do. Sometimes change is needed, and that’s when Timber reintroduced us.
I get that WordPress isn’t the perfect tool for everything or everyone, but then what is? There’s a time and a place for it, and I’m hoping to map out some roads you might have missed.
What is Twig?

Twig is a wonderful template language for PHP. Don’t let the name fool you; it’s anything but brittle.
If you’ve ever seen any JavaScript template languages, a-la Handlebars or Moustache, then this will look familiar. If not, don’t fret; Twig is a joy to behold by giving you a concise, accessible syntax to work with. Just have a look this example from their home page:
<?php echo $var ?>
<?php echo htmlspecialchars($var, ENT_QUOTES, 'UTF-8') ?>
{{ var }}
{{ var|escape }}
{{ var|e }} {# shortcut to escape a variable #}
Tell me that didn’t spark some excitement!
To echo a variable, you just wrap it in double curly braces while omitting the dollar sign e.g. {{ var }}
. You can also access the attributes of a variable like so: {{ user.name }}
Control structures appear within {% ... %}
blocks, and are just as friendly to wield:
{% for post in posts %}
{{ post.title }}
{% endfor %}
You can learn more from their documentation.
Imagine writing your WordPress templates like this…
What is Timber?

Timber is the plugin that unites WordPress with Twig. Along with the benefits of using Twig, this lets you separate the PHP from your HTML templates. This in turn give you a more breathable environment for you to develop your theme as we’ll explore in a moment.
Let’s have a look at an example from the documentation, and then we’ll step through it:
$context = Timber::get_context();
$context['post'] = new TimberPost(); // It's a new TimberPost object, but an existing post from WordPress.
Timber::render('single.twig', $context);
The `single.twig` file:
<article>
<h1 class="headline">{{post.post_title}}</h1>
<div class="body">
{{post.content}}
</div>
</article>
Starting with `single.php`, the first thing we’re doing is fetching the context of the theme with Timber::get_context();
. This object will contain such things as your menus, wp_head
, and wp_footer
. Later on we’ll look at how to add to this if you need anything else globally accessible in your theme. You’ll be using this line a lot.
Next we’re going to need to get the post we want to display in our template. Using new TimberPost();
will figure out which post to fetch.
Finally, we want to display something. Timber’s render
function will call on our Twig file and pass through the data we’ve just collected.
One final bombshell, and I hope it’s not too much: Timber comes with baked-in support for Advanced Custom Fields.
Why use Twig?
After a while with WordPress, you might find you can’t see the wood for the trees amidst a confusing dance between PHP and HTML. This isn’t to say all themes are like this, but you can see how easily it can happen.
What I found rather lovely about Timber is that it mitigates this potential problem by creating space between the two. You’re never mingling PHP with HTML, but they’re still talking to each other. You may find the conversations more meaningful as this gives the best parts of WordPress the opportunity to shine through.
For example, our `single.php` file is just 3 lines, and that’s only for dealing with our data. By the time that data reaches our view, we’re happy knowing we have all we need – all that’s left is to mark it up.
Getting Started
Installation
So let’s get stuck in! You can install this via composer
if you’re so inclined, but for the sake of brevity I’m going to install it via Plugins > Add New, and do a keyword search for Timber and Twig. Alternatively, you can grab it from the Plugin Directory and upload it to your site.
Starter Theme
Timber ships with a barebones starter theme, which is perfect for hitting the ground running. Once the plugin is installed, you’ll find the following folder which you can copy to your themes folder. This should be located in `wp-content/plugins/timber/timber-starter-theme`. Or, if you just want to have a peek, you can browse the starter theme repository.
You’ll notice on first glance that there’s nothing out of the ordinary going on here. Where’s the magic at? It’s alright to feel apprehensive at this point, so lets dig a little deeper.
functions.php
The purpose of your `functions.php` file will largely remain the same. You can use it how you normally would, be it for declaring custom post types, or defining your custom functions. However, with the Timber Starter Theme, you’ll notice a class declaration awaits inside. The most notable method of this class is add_to_context
.
From here we can add items that we want to access throughout our entire theme. Say for example I created a menu within the Admin Dashboard with a slug of primary-menu
, I could add it to the global context like so:
$context['primary_menu'] = new TimberMenu('primary-menu');
Now I can access it like this:
<nav role="navigation">
<ul>
{% for item in primary_menu.get_items %}
<li class="{{item.classes | join(' ')}}">
<a href="{{item.get_link}}">{{item.title}}</a>
</li>
{% endfor %}
</ul>
</nav>
Not too shabby! Another example I’d like to share is when using something like this multi-environment config. This creates a constant for denoting the current environment such as development, staging or production. I can pull this information into my theme easily by adding the following to my add_to_context
method:
$context['env'] = WP_ENV;
Within my templates I can call {{ env }}
to tell me which environment my site is running on.
Extends and Blocks
Let’s have a look at `index.php`.
$context = Timber::get_context();
$context['posts'] = Timber::get_posts();
$templates = array( 'index.twig' );
if ( is_home() ) {
array_unshift( $templates, 'home.twig' );
}
Timber::render( $templates, $context );
Ok, so most of this we’ve seen before. There’s a little extra going on here with having an array of templates. All this is doing is adding `home.twig` to the start of the array if is_home()
returns true.
Lets say is_home()
returns false, so that’ll tell it to render `index.twig`. Twig files live within the `views` directory, so we’ll begin there, in our `index.twig` file:
{% extends "base.twig" %}
{% block content %}
{% for post in posts %}
{% include 'tease.twig' %}
{% endfor %}
{% endblock %}
There’s a couple of new things to introduce here, namely extends
and block
. If you’re not sure what those do, I’m sure you’ll twig it soon enough.
When we call extends
, we’re saying “use this layout for any content that I declare in this template”. Then within `base.twig` you’ll have something like this:
<!doctype html>
<html>
<head>
...
</head>
<body>
...
{% block content %}
No content found!
{% endblock %}
</body>
</html>
Usually with WordPress we might just include our footer and header in each template, but here it’s all in the one file which we tell our template to extend.
You can name your blocks whatever you wish. That string of “No content found!” will only display if nothing is supplied in a content
block. In the Timber Starter Theme you’ll see they have extra blocks declared for parts of the header and footer, but it’s all about using what makes sense for you.
Custom Template
Enough of this loose talk, it’s time to hand-roll our own template! This walkthrough will make use of Advanced Custom Fields, as well as a few extra features of Timber + Twig that are sure to delight.
We’re going to create a very simple example of a page that has a banner image with some text laid across it. A hero element, if you will. Our hero will reveal the expressiveness of Twig to us, and challenge our mental model of WordPress as we know it. We’ll imagine our hero element has been defined using Advanced Custom Fields. You can call a custom field with the following: post.get_field('field_name');
. If you’re using WordPress’ own custom fields then it’s as easy as post.field_name
.
There’s a couple of ways to go about creating a custom page template, and because in my case I won’t always know the final URL, I’m going down the “Custom PHP File” route. Finally, let’s start typing things!
base.twig
For our base layout, I’ve combined Timber’s starter theme layout file with HTML5 Boilerplate’s index file:
<!doctype html>
<html class="no-js" {{site.language_attributes}}>
<head>
<meta charset="{{site.charset}}">
<title>
{% if wp_title %}
{{ wp_title }} - {{ site.name }}
{% else %}
{{ site.name }}
{% endif %}
</title>
<meta name="description" content="{{site.description}}">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="{{ site.theme.link }}/style.css">
</head>
<body>
<nav role="navigation">
<ul>
{% for item in primary_menu.get_items %}
<li class="nav-item {{item.classes | join(' ')}}">
<a class="nav-link" href="{{item.get_link}}">{{item.title}}</a>
</li>
{% endfor %}
</ul>
</nav>
<div class="wrapper">
{% block content %}
No content found!
{% endblock %}
</div>
</body>
</html>
Calling on {{ site.theme.link }}
can be pretty useful, and if you’re using Wiredep + Gulp, you can look at this snippet to see how you might blend it into your workflow.
Custom Template Files
As we’ve seen already, your PHP file is going to be simple. A few lines in `your-custom-page.php` should do it:
<?php
/*
* Template Name: Your Custom Page
*/
$context = Timber::get_context();
$post = new TimberPost();
$context['post'] = $post;
Timber::render( 'your-custom-page.twig', $context );
That’s all we need! Our hero `your-custom-page.twig` file awaits…
{% extends 'base.twig' %}
{% block content %}
<section>
<h1>{{ post.get_field('hero_title') }}</h1>
<img src="{{ TimberImage(post.get_field('hero_image')).src }}" alt="{{ TimberImage(post.get_field('hero_image')).alt }}">
</section>
{% endblock %}
We’re using TimberImage
here to grab the information we need about the image. In this case it’s the URL and the alt text.
We could leave it there… but as we’re already on a roll, we’re going to take advantage of the resize
filter. This takes 3 arguments: width, height, and crop. We’re only going to be specifying the width.
Let’s refactor our hero image to be a bit more responsive:
<img alt="{{ TimberImage(post.get_field('hero_image')).alt }}"
srcset="
{{ TimberImage(post.get_field('hero_image')).src | resize(1600)}} 1600w,
{{ TimberImage(post.get_field('hero_image')).src | resize(1000)}} 1000w,
{{ TimberImage(post.get_field('hero_image')).src | resize(700)}} 700w
"
src="{{ TimberImage(post.get_field('hero_image')).src | resize(1000)}}"
sizes="100vw"
>
Yes, you can do that.
Conclusion
WordPress has much to offer, and I’m guilty of taking it for granted. Loving what you do is a reward in itself, and I believe that taking some time to approach something in a new way is a great opportunity to cultivate some new skills, and to re-invigorate motivation. I can’t tell you how many sites I’ve built with WordPress, but I can tell you the exact moment my mind was blown when I first used Timber.
This is a great writeup, thanks for the overview TJ!
Glad you enjoyed it! Thanks :)
I love using Timber and have been using it for the past year. As a front-end developer with not much php coding experience timber just makes everything obvious and really easy to understand.
I’ve never used Timber, but I love the responsive images example. It really seems to get around WordPress’s frustrating preset thumbnail size logic.
One thing that worried me when I started using Timber was the idea I’d have to reconstruct all the standard WordPress function calls like the_title(). No, Timber has you covered. Just wrap any WP function call in a {{function()}} tag. Eg, {{function(‘wp_title’, ‘any parameter’)}}. Simple.
I tried Timber but did not know about this and somehow missed it in the docs, so I stopped using it.
Time to give it another try!
Actually, TJ, if you were to consider a follow-up post, I’d humbly request some discussion on how to integrate dynamic sidebars/widgets. The docs on the Timber site seem very incoherent, and it usually takes me a couple trips to Stack Overflow to get widgets working.
Hi Nick,
I can give you an example of a project I’m currently working on. There’s a couple of steps which I’ll outline:
In
functions.php
you would initialise a widget like so:After that you can add whatever you’d like via the dashboard. Then within your PHP file you can add it to the context/data by calling the name you assigned it:
Then, finally, within your .twig file you can access it like this:
Hope that helps!
Output should be escaped by default, IMO.
For most of my projects, I have switched from WordPress to https://Bolt.cm .
Bolt uses Twig natively and is more flexible and user-friendly then WordPress. If you appreciate the advantages of Timber and Twig, it’s likely that you’ll prefer switching to Bolt entirely as well for your next project.
Sometimes WP is unavoidable though, and Timber is a good solution for these occasions. Thanks for this article, I learned at least 2 new tricks here, and will bookmark.
Bolt does look quite nice! What I was trying to convey, though, is that WP doesn’t have to be this thing that we put up with. It really is quite expressive, and sometimes we can lose sight of that be it from popular opinions or a bad experience that coloured our impressions.
However as with everything else, it’s not for everyone, and doesn’t have to be. I’ll be giving Bolt a try, and I’m looking forward to it.
Looks interesting! How do you handle projects that normally are solved through the use of plugins, such as Woocommerce for simple e-commerce integration, or Gravity Forms for simple to advanced form integration?
One of wordpress’ core strengths is the plugin community and easy extensibility. How does Bolt handle that or compare?
@Lars Faye :
Extensions/plugins and themes are available at http://extensions.bolt.cm * and can also be installed via a few clicks in the Bolt back-end. For most of the extensions, functionality is separated from layout as much as possible.
Things like custom fields or -contenttypes, routing or extended user/group management are available by default so you may need less plugins then you would expect. For Gravityforms-like functionality, the Boltforms extension is a good choice.
Bolt is not aimed at building a online shops, but I’ve see people use Bolt in conjunction with services like Shopify.
*)looks like this site is undergoing maintenance as I write this, that could have been why you missed it.
You should check out Laravel’s Blade syntax. Just as useful and elegant.
http://laravel.com/docs/5.1/blade
I’m a big fan of Laravel, and Blade brings me great joy any time I get to use it.
@Evert Albers
Yeah, when you Google about ‘WordPress alternative’ your name pops up on about every so called ‘useful’ website telling people the same story.
Ontopic,
I don’t know about this Twig stuff.. It really doesn’t give me the nice feeling like PHP function tags in-between HTML tags does. When I started creating static HTML websites years ago I really had this huge Oo boy a CMS problem. I only had some knowledge about HTML and CSS, which was pretty easy to learn and the results where great/satisfying for beginners speaking. Creating a CMS was really something bold and technically. Everything went great, the problem was updating all those static sites, people always had to ask me every single time they needed a change at their website so I started to look into WordPress because everybody in the world was saying, this is it, for starters, and it was… Some folks like to trash about WordPress a lot and how bloated and overkill it can be, but to be honest I never had this feeling in the last few years and there isn’t anything like WordPress out there today.
I have been searching for months now to make a switch and leave WordPress behind but for some reason it’s just not doable and I always keep going back and eat all the fancy junk the Core developers come up with every new release. They still create some very solid code but there is so much junk inside WordPress now that isn’t really needed.
I mean WordPress functions so well, even without a lot of PHP knowledge and only solid HTML/CSS knowledge you can create your own theme from scratch, the PHP functions tags are readable and understandable what they do. or tell you exactly what to expect there, and it can’t get any easier than that.. Just wrap those PHP code/functions between HTML tags and you’re done. If you understand the WordPress template hierarchy you’re almost half done.
This is how lovely it was when I started creating WP themes back in the Twenty Ten (theme) area. Nowadays the WordPress template structure has become very PHP boring like, less HTML in the templates and lots of this, <?php the_title('’, ”) ?>. If it has come to this, boy, I can only say you have lots grounds in your ‘easy to use’ some way down the road. This is only a very simple example there is way more ugliness to be shown when it comes to WordPress but I have too much respect for the platform and feel bad for even saying this.
The entire Customizer.js junk is the thing I hate most. Seriously, if you need a haf done front-end, small, sidebar kinda toolbar to change your site name or color I think you’re in deep trouble. This can maybe functions very well at wordpress.COM but in the self host ?? I don’t think so. I understand that dashboards will come to an end at some point and we are going to post + edit directly from the front-end but this WordPress Customizer thing isn’t the way to go forward in a CMS environment. But hey, if some people like it, go ahead, but don’t Apple-throat me with it. If I don’t like to use the Customizer in my themes at this point, I can’t simply remove it from the core function with just a single filter in the functions.php, No I have to install a plugin, or write 35 lines of code included custom CSS to the admin to hide everything that has something to do with the Customizer.
It’s not getting more pretty over at WordPress today. I believe the Core developers are creating too much fancy stuff they personally really like instead of creating what the community really needs.
I really wish there was something ‘new’ and very basic out there that is really focus on front-end development but still has some sort of dashboard, back-end integration so that people can make some changes to their sites. Maybe something with a solid Post Types foundation because the way how Custom Post Types integration work in WordPress is simply beautiful. I just don’t need all the fancy stuff around it.
Damn, I have loved WordPress so much over the past few years but it really has come to a point where it’s simply less fun and pretty anymore.
@shmoo:
I find it slightly ironic that you berate Evert Albers for pointing out Bolt to people that might not know it, followed by some points you dislike in the current state of WordPress.
Funny thing is, each and every one of the things you mention is something that we’re doing differently and better in Bolt.
Unless you do some really hacky shit, Twig (and CMS’es using Twig) will not allow you to use PHP functions inside templates. While this might seem like you have less freedom, it actually promotes good practice: Seperation of concerns between the business logic (PHP stuff that should be handled by PHP/controllers/the CMS) and presentation (the HTML).
Most Bolt configuration is done through YML files. This might seem like a hassle, but it’s extremely flexible and easy to use. No clinky checkboxes or fiddling with millions of inputs. Just modify the
.yml
in your browser or favorite editor. As an added bonus, you can just keep these files in your versioning system, for easier development and deployment.Ever since we’ve started we’ve stated very firmly that we’re not going to go into that direction. Basically there are three types of users for every CMS: Editors, frontend devs and backend devs. Each one of them is equally important. We’ve even put that in our manifesto.
This is pretty much what Bolt is.
Those are called Contenttypes in Bolt, and are very quick and simple to modify to your site’s needs. (see the point about .yml, above)
Seriously though, you should check out Bolt and join us on the IRC channel. We’re a friendly bunch, always willing to help out newbees.
Great article, thanks TJ! I’ve got only one question your article hasn’t dealt with as well the official Timber documentation. How do you handle conditional functions like is_front_page, is_page() (e.g. is_page(7), is_404 and so on.There are quite a handful in the WordPress codex.
In the Timber docs they aren’t mentioned. What is the best practise how to deal with those? Move the if logic to the PHP files and don’t use the if else functionality inside Twig for it? Or stay in Twig and use a special syntax instead?
I haven’t tried this but it works for other things: you could wrap the function in a Timber {{ function }} tag and use a conditional to set a variable inside Twig? like
Then you can use repeatably inside the rest of the template in other conditionals.
You can also re-implement PHP functions as Twig functions, but that’s a bit outside my comfort zone.
TJ, thanks for clearly setting out that example, it’s really good to have a concise reference.
Twig is awesome! Why not go all-in and just use Craft? Then you get to save yourself all the other headaches that WordPress gives you. ;)
+1 Craft is far nicer to use.
“Just use _______”
Seriously, +2 for Craft.
WP is great in some situations but if you’re going to use ACF for fields, plus Timber to get Twig, that’s 2 more dependencies you now have to rely on for every build.
I usually end up disabling comments, and adding in a few custom post types, which you’ll probably need 1 or 2 more plugins to accomplish depending on what kind of sites you’re building. So now you’re at 4 plugins, minimum. You’ll probably want some kind of caching solution as well down the road.
Craft has all of that built in, combined with a better interface. In a client/designer situation, it works wonderfully but obviously YMMV. If you use prebuilt themes like Genesis or something on Themeforest, you probably won’t get what Craft is about but anyone who rolls their own HTML and CSS and cares what markup their CMS is spitting out will love Craft IMO.
Checkout something I’ve been working on
https://github.com/imranismail/rapido
Hi Imran, thanks for sharing this! I’m really digging it.
Just use MODX, has had these features for years…
http://modx.com/
“Just use _______”
I’ve been working with Symfony for a while and many of you know that Twig is the template engine of choice there. Twig is incredibily powerful and flexible, gives a ton of freedom.
But that’s the problem: it’s too powerful. It can do a lot, it can do too much. You can put logic in your templates with Twig. It’s so easy to misuse it.
If you think about it, then what’s holding you from using directly PHP for what it’s been conceived for, i.e. templating pages? Why adding a slower layer altogether? In fact, in Symfony you can use PHP instead of Twig.
I still use Twig, but I’m more and more convinced that a template language like Handlebars should be enough for everyone’s templating needs.
Hi, great article, one question performance wise does timber make wordpress work slow (speecially in shared servers) or is barely noticeable and about memory consumption does it increase to much? or is basically the same?
Hi Jairo,
Thanks! Personally, I haven’t noticed any performance hits when using Timber. The plugin author goes into detail on the Performance section of the documentation, where it states there’s little to no performance hit.
All of this becomes much less of a concern when you’re using a caching plugin that serves a static page, thereby circumventing the rendering process for Twig.
I can’t speak for every server out there, just my own experience which has been really good so far.
I also love Timber. I mostly develop themes using Timber and branch and I’m the developer of branch which is a WordPress starter theme based on Timber + Bootstrap.
Please do check this out.
Thanks for this article. One advantage I see with using Twig is that most of your template code can be recycled for templating languages with similar syntax, like Mustache, Liquid or Swig.