Image of a twig attached to a tree, on a green background

A quick run-through of one the many exciting new features that Twig brings to Drupal 8: extendable, easily-overridden templates.

One of the many good things about Drupal 8 is the introduction of Twig. It's a lovely templating engine that, in my opinion, is far superior to PHPTemplate. For us frontenders, it has a much nicer user syntax that's more akin to handlebars or other JS templating engines.

Imagine this scenario (I'm sure many developers have encountered this before):  you're building a site and some of your page templates need to be full-width, while others have a width-restricted content section. Furthermore on some pages you want to add a class to change your nav's styling.

The Drupal 7 way went something like this:

  • Take page.tpl.php and duplicate it, e.g. page--featured-content.tpl.php
  • Add the new wrapper to the new template
  • Clear the cache
  • Job done

This is fine, but is definitely WET rather than DRY. What happens when the markup for say the footer, or the header, needs to change? The answer is a mass find / replace, leaving you open to missed pages, etc.

Thanks to Twig we have a much cleaner solution for this. Twig brings to the table the concept of blocks (not to be confused with Drupal blocks!). You can define a named section in a template that can be overridden by a sub template. What this means is rather than copying / pasting the whole file and changing one line, you simply override that one block in your new file.

Let's start with the wrapper example from above…

First we create a named block to hold the content we want to override:

{% block header %}
  {% set alt_header = FALSE %}
  {# Header code #}
{% endblock %}

{% block wrapper %}
  {% block content %}
    {# Content code #}
  {% endblock %}

  {% block footer %}
    {# Footer code #}
  {% endblock %}
{% endblock %}


Now for our page--featured-content.html.twig we can just do:

{% extends 'page.html.twig' %}
{% block wrapper %}
  <div class="container">
    {{ parent() }}
  </div>
{% endblock %}


That first line just tells Twig which template to use as a base. The new content block will be used in place of the base template, and the rest will stay the same.

The parent() line is pretty cool: it tells Twig to just whack the content of the 'master' block in the new block, so you don't have to retype the whole thing. If you have nested blocks (as in this example), it doesn't matter, you just say "this in here, thanks".

What about variables?

In the example of our dark nav, our Twig template has this:

{% set dark_nav = FALSE %}
{% block header %}
  {% set nav_class = '' %}
  {% if dark_nav %}
    {% set nav_class = 'header--dark-nav' %}
    <header class="header {{ nav_class }}">
      {# header markup here #}
    </header>
  {% endif %}
{% endblock %}

We simply declare the variable outside of the block, and then in the new block redefine it, e.g.: 

{% block header %}
  {% set dark_nav = TRUE %}
  {{ parent() }}
{% endblock %}


So there we have it, in a nutshell. All our new templates can inherit all the defaults from one base, and only have the customisations we need in the sub templates.

Life is better, and updating blocks is just a case of directly editing one place. Happy days!

Tags

drupal planet frontend drupal