The Twig for Timber Cheatsheet

Posted October 16, 2015 in Tutorials+Tips, WordPress


Timber is a library for WordPress that allows you to work with object oriented templates using the Twig templating engine. I’ve written about it before, and have used it on just about every project I’ve done in the last year and a half. I’m even teaching a class on it!

Anyhow, although Timber ultimately simplifies templating in WordPress, there’s a bit of a barrier to entry. If you haven’t worked in a templating language before, it takes some time to get a handle on what’s possible. The following are some introductory Twig concepts and snippets I’ve found particularly useful when working with Timber and Advanced Custom Fields.

Conditionals

Let’s say you have an ACF option for the display of the header that is either an image or text. You could check like this:

<header class="single-header">
    {% if post.header_display == 'Image' %}
        {# Markup for header image #}
    {% else %}
        {# Markup for plain text header #}
    {% endif %}
</header>

Shorthand Conditional

That’s all well and good, but what if you want to put a class on the <header> tag that indicated the display choice? It’s pretty gross looking to do it this way:

<header class="single-header {% if post.header_display == 'Image' %}header-image{% else %}header-text{% endif %}">

Luckily, we can use shorthand conditionals!

<header class="single-header {{ post.header_display == 'Image' ? 'header-image' : 'header-text' }}">


Much better. In this case, the ? says, “if the thing in front of me is true, then do this”, and the : stands for the else.

So, if I wanted to check if someone was hungry I would do:

{{hungry ? 'Get some sushi'}}

And if I wanted to add an else, it would be with the : :

{{hungry ? 'Get sushi' : 'No sushi'}}

Setting a Variable

Many times in Timber you will need to get the Timber Object, and it can be a lengthy thing to type, particularly if you are dealing with filters. Luckily, we can throw those bad boys in some variables! Let’s say you have a custom field for a header image and want to resize it in the template. You can set that to a variable like this:

{% set header_src = TimberImage(post.header_image).src|resize(1200, 900) %}

And call it like this:

<img src="{{header_src}}" alt="Header image for {{post.post_title}}">

To take this a step further, we can use the variables for responsive images using srcset. Keep these out of the way at the top of your Twig file:

{% set large_img = TimberImage(post.header_image).src|resize(1200, 900) %}
{% set medium_img = TimberImage(post.header_image).src|resize(800, 400) %}
{% set small_img = TimberImage(post.header_image).src|resize(450, 300) %}
{% set img_alt = TimberImage(post.header_image).title %}

Then in our actual markup, we would have:

<img srcset="{{small_img}} 375w,
             {{medium_img}} 780w,
             {{large_img}} 1024w"
    alt="{{img_alt}}">

Ahhh…much nicer.

For Loop

Of course, since we are working within WordPress, we’re going to have loops all over the place. The fundamental syntax for a loop is:

{% for post in posts %}
    <h3>{{post.post_title}}</h3>
{% endfor %}

But what is post and posts? I always find it hard to tell what names are arbitrary (meaning I name them) vs. ones that are meaningful (meaning they need to be called that). You could look at it like this:

{% for placeholder_name_you_decide in array_you_got_from_php_file %}
    <h3>{{placeholder_name_you_decide.something_in_the_object}}</h3>
{% endfor %}

With that in mind, in the posts loop I decided to call each item post while the name for the array itself, i.e. posts, was decided in the corresponding PHP file.

Another example would be a repeater field. It could be set up like in the Admin like this:

acf
A Repeater in Advanced Custom Fields.

To loop through the repeater in your template, you would do this:

{% for member in post.get_field('team_members') %}
    <p>{{member.member_name}}</p>
{% endfor %}

I usually wrap that in a conditional as well just to be sure:

{% if post.team_members %}
    {% for member in post.get_field('team_members') %}
        <p>{{member.member_name}}</p>
    {% endfor %}
{% endif %}

Note that you need to call get_field() on the repeater array in order to return all of the repeater field data. To be honest, I’m not entirely sure why that is, but take my word for it. If you understand it please enlighten me in the comments!

Slicing An Array

While we’re on the topic of loops, it’s definitely possible you’ll want to limit the number of posts at some point, or maybe show one post at the top and the rest on a different part of the page. In regular ‘ol WordPress PHP this would be a pain in the behind, or at least there are many ways to do it. In Twig, however, we can do something like:

{% for post in posts|slice(0,1) %}
    <-- Some fancy post markup for a highlighted post -->
{% endfor %}

<-- ... other content on the page here ... -->

{% for post in posts|slice(1,5) %}
    <-- Some not as fancy markup for more minor posts. -->
{% endfor %}

That’s not to say you shouldn’t limit how many posts you are requesting in the PHP files; even though you have lots of control over the number of posts in the Twig file, you still want to have a posts_per_page in your query and request only the amount of posts you need.

Using Includes

Use an include when you have code that will appear in many places. By separating it into it’s own file, you can avoid rewriting it over and over.

{% include 'partials/prev-next.twig' %}

Passing Variables

You can also pass a variable into an include. For example, let’s say you want to have different text for previous/next links depending on the post type. You could include something like this:

{% include 'partials/prev-next.twig' with { prev_text: 'Older', prev_text: 'Newer' } %}

In the partial, you would have:

<nav class="prev-next-nav">
    {# Note that the TimberPost Object passes the data for your previous and next links. How awesome is that? #}
    <a href="{{post.prev.link}}">{{prev_text}}</a>
    <a href="{{post.next.link}}">{{next_text}}</a>
</nav>

In this case, prev_text would be replaced with the “Older” text you defined in the include. Then, for a different post type maybe “Older” and “Newer” don’t make sense. You can use the same partial, but pass in different text:

{% include 'partials/prev-next.twig' with { prev_text: 'Previous', prev_text: 'Next' } %}

That’s all for now!

I could add a bunch more to this list, but let’s save that for another day when I muster the energy to add better syntax highlighting for Twig.

Comments

What do you think? Do you have any questions, thoughts, or related links to share? Did I make a mistake in my post?

Submit a Comment

Pssst...developer note: This is the most basic comment implentation you can possibly imagine, and it's on my list to flesh it out. But...I'm not using Disqus anymore, so that's cool! 👍