Drupal Code Standards: Twig in Drupal 8

This is the seventh post in a series about coding standards. In this post we’ll talk about how to adhere to standards while implementing Twig templating in Drupal 8.

Other posts in this series:

  1. Drupal Code Standards: What Are They?
  2. Drupal Code Standards: How Do We Implement Them?
  3. Drupal Code Standards: Formatting
  4. Drupal Code Standards: Documentation
  5. Drupal Code Standards: The t() function
  6. Drupal Code Standards: Object Oriented Coding & Drupal 8
  7. Drupal Code Standards: Twig in Drupal 8

The DocBlock

Twig files should include a docblock like any other Drupal file. A docblock is a specially formatted block of information that goes at the top of every file, class, and function. See our post on documentation to brush up if you’ve forgotten the finer points of docblocks!

Sections such as @see, @ingroup, etc, still apply as they did before Twig, so use as appropriate. You can consult our guide here or the Drupal documentation.

A note on @ingroup themeable from Drupal.org:

Twig template docblocks should only include @ingroup themeable if the template is providing the default themeable output. For themes overriding default output the @ingroup themeable line should not be included.

Variables

Variables should be referenced only by name, with no prefix. For example, foo instead of $foo or {{ foo }}. The type should not be included, as this does not affect theming.

Here’s an example from the Token contrib module:

{#
/**
* @file
* Default theme implementation for the token tree link.
*
* Available variables:
* - url: The URL to the token tree page.
* - text: The text to be displayed in the link.
* - attributes: Attributes for the anchor tag.
* - link: The complete link.
*
* @see template_preprocess_token_tree_link()
*
* @ingroup themeable
*/

#}
{% if link -%}
{{ link }}
{%- endif %}

In the above code, you can see that the docblock lists the available variables. They are listed without a prefix or type. This is the default template, so it uses @ingroup themeable. Also notice that there is no period at the end of any line starting with a @ tag.

Variables referenced inline in a docblock should be wrapped in single quotes.

Here's an example from Drupal 8 core, the Comment module:

/*
...
* - created: Formatted date and time for when the comment was created.
* Preprocess functions can reformat it by calling format_date() with the
* desired parameters on the 'comment.created' variable.
* - changed: Formatted date and time for when the comment was last changed.
* Preprocess functions can reformat it by calling format_date() with the
* desired parameters on the 'comment.changed' variable.
...
*/

The above code is from comment.html.twig, which has a very lengthy docblock, but you can see from this snippet that these variables are properly referenced and wrapped in single quotes.

Expressions

Twig makes it easy to check if a variable is available before using it. Just use if. Here's an example from Drupal 8 core:

  ___TWIG0___
<h2___TWIG1___>___TWIG2___</h2>
___TWIG3___

In the above code, we can see that the code checks for the availability of label with if label before printing it. This is a lot simpler than previous methods of checking, which required more complex structures and more code.

Loops are also much simpler in Twig! You can easily use for loops in Twig. Here's an example from the Devel contrib module:

___TWIG0___
<div class="sf-toolbar-info-piece">
<span><a href="___TWIG1___" title="___TWIG2___">___TWIG3___</a></span>
</div>
___TWIG4___

The above code will loop through and print out each item in collector.links.

If you need to use a key and a value in your for loop, you can accomplish that as well. Here's an example from the Ctools contrib module:

  ___TWIG0___
&lt;div class="wizard-trail"&gt;
___TWIG1___
___TWIG2___
<strong>___TWIG3___</strong>
___TWIG4___
___TWIG5___
___TWIG6___
___TWIG7___
___TWIG8___
___TWIG9___
___TWIG10___
&lt;/div&gt;
___TWIG11___

In the above code, you can see that for key, value in trail gives us the expected key and value pair for a foreach loop, and they can be used throughout the body of the loop.

Another simple expression that can be done in Twig is variable assignment. If a variable is needed only in the template, it can be declared directly, as you would anywhere else. Like this:

___TWIG0___

For more on how to translate variables, see our post on the t() function, and the section on Drupal 8 & Twig.

HTML attributes

HTML attributes in Drupal 8 are drillable. They can be printed all at once, or one at a time, using dot notation. If you do not print them all, they should all be included at the end, so that any other attributes added by other modules will be included. If you're not familiar with HTML attributes in Drupal 8, here's some Drupal.org documentation, and the Drupal Twig documentation on HTML attributes. Here's an example from Drupal 8 core:

___TWIG0___
<a___TWIG1___>___TWIG2______TWIG3___</a>
___TWIG4___
<span___TWIG5___>___TWIG6______TWIG7___</span>
___TWIG8___

In the above code, the full attributes are printed for the <a> and <span> tags.

You can also add or remove a class in Twig. Here's some documentation on Drupal.org, and here's an example from the Views module:

___TWIG0___
<div___TWIG1___>

In the above code, an array of classes is created and added to the HTML attributes.

Whitespace Control

The {% spaceless %} tag removes whitespace between HTML tags. Wrap your code in this tag wherever you want to remove whitespace.

Here’s an example from the Devel contrib module:

___TWIG0___
<div class="sf-toolbar-info-piece">
<b>___TWIG1___</b>
<span class="sf-toolbar-status sf-toolbar-status-___TWIG2___">___TWIG3___</span> ___TWIG4___
</div>
<div class="sf-toolbar-info-piece">
<b>___TWIG5___</b>
___TWIG6___
</div>
<div class="sf-toolbar-info-piece">
<b>___TWIG7___</b>
<span>___TWIG8___</span>
</div>
___TWIG9___

The whitespace control character (-) removes whitespace at the tag level. Here’s an example from Drupal 8 core:

<span___TWIG0___>
___TWIG1___
___TWIG2___
___TWIG3___
</span>

In the above code, we can see the - character added to the delimiters, indicating the whitespace should be removed. This can also be used to remove the space from both sides or just one, if the character is only on one side.

Caveat regarding newlines at the end of files

Drupal coding standards require that all files have a newline at the end of files, and if you have PHP codesniffer or any other tests set up for Drupal standards, it will require this. However, in Twig, this may not be wanted in your template output. Until a better community-wide solution is reached, you can alter your tests if you need them to pass, or add a twig template tag to the end of the file - you can read this issue for more clarification, as it is an ongoing issue.

Filters

A filter in twig uses the pipe character - |. In our post on the t() function, we talked about using the new |t filter to translate text, but there are other filters that you can use as well. Here's an example from Drupal 8 core:

<li class="project-update__release-notes-link">
<a href="___TWIG0___">___TWIG1___</a>
</li>

In the above code, we can see a simple |t filter. The text 'Release notes' is being piped through and is now available for translation, as well as being escaped and safely processed.

There are a variety of Twig filters and Drupal-specific filters.

Here's an example of a Twig filter, join, from Drupal 8 core:

<div class="sf-toolbar-info-piece">
<b>___TWIG0___</b>
<span>___TWIG1___</span>
</div>

In the above code, the join filter is applied to collector.roles. It concatenates the items and separates them with a comma and space, as indicated.

Comments

Comments are wrapped in the Twig comment indicator, {# ... #}. Short and long comments use the same indicator, but long comments should be wrapped so that they do not exceed 80 characters per line. Comments on one line should have the indicator on the same line. Comments that span several lines should have the indicators on separate lines. Here's an example of a short comment from Drupal 8 core, the Field UI module:

___TWIG0___

Here's an example of a long comment from Drupal 8 core, the Book module:

    ___TWIG0___

Syntax

These standards are taken from the Twig Documentation.

  1. Put one (and only one) space after the start of a delimiter ({{, {%, and {#) and before the end of a delimiter (}}, %}, and #}). 1a. When using the whitespace control character, do not put any spaces between it and the delimiter.

  2. Put one (and only one) space before and after the following operators: comparison operators (==, !=, <, >, >=, <=), math operators (+,-, /, *, %, //, **), logic operators (not, and, or), ~, is, in, and the ternary operator (?:).

  3. Put one (and only one) space after the : sign in hashes and , in arrays and hashes.

  4. Do not put any spaces after an opening parenthesis and before a closing parenthesis in expressions.

  5. Do not put any spaces before and after string delimiters.

  6. Do not put any spaces before and after the following operators: |, ., .., [].

  7. Do not put any spaces before and after the parenthesis used for filter and function calls.

  8. Do not put any spaces before and after the opening and the closing of arrays and hashes.

  9. Use lowercase and underscored variable names (not camel case).

  10. Indent your code inside tags using two spaces, as throughout Drupal.

Syntax Examples

Here’s an example from Drupal 8 core demonstrating #1a:

<article___TWIG0___>
___TWIG1___
___TWIG2___
___TWIG3___
</article>

In the above code, on line 3, you can see that there is no space between the delimiter, {{, and the whitespace control character, -.

Here’s an example from the Display Suite contrib module demonstrating #2 and #4:

___TWIG0___
___TWIG1___
___TWIG2___
___TWIG3___
___TWIG4___
___TWIG5___
___TWIG6___
___TWIG7___
___TWIG8___
___TWIG9___
___TWIG10___
___TWIG11___

In the above code, you can see that all of the comparison operators throughout the snippet have one space on either side. You can also see that the expressions, like (left and not right) do not have any spaces after their opening parenthesis or before their closing parenthesis.

Here's an example from Drupal core, the Classy theme, demonstrating #3, #8:

___TWIG0___

In the above code, you can see that there are no spaces before or after the opening or closing of the array (['odd', 'even']), and there is only one space after the comma in the array.

Here’s an example from the Ctools contrib module demonstrating #1, #5, #6, #9, #10:

___TWIG0___
<div class="wizard-trail">
___TWIG1___
___TWIG2___
&lt;strong&gt;___TWIG3___&lt;/strong&gt;
___TWIG4___
___TWIG5___
___TWIG6___
___TWIG7___
___TWIG8___
___TWIG9___
___TWIG10___
&lt;/div&gt;
___TWIG11___
</pre>

In the above code, you can see that there is only one space after the start of the delimiter, `___TWIG12___`, throughout the code snippet.

On line 7, you can see that there is no space before or after string delimiters `___TWIG13___`.

On line 9, you can see that there is no space on either side of the pipe operator.

Lower-case variable names are used throughout the snipped, as well as proper indentation.

Here’s an example from [the Devel contrib module](https://www.drupal.org/project/devel) demonstrating <a href="#q7">#7</a>:

```twig
<div class="sf-toolbar-block">
<div class="sf-toolbar-icon">___TWIG14___</div>
<div class="sf-toolbar-info">___TWIG15___</div>
</div>

In the above code, you can see that there are no spaces on either sides of the parentheses. The space after is technically the space before the end of the delimiter.

Wrapping Up

Twig is fairly new to many of us in the Drupal community, so let us know what you think! Once you get the hang of it, it really simplifies a lot of things. Have questions/comments/concerns? Experience you’d like to share? Talk to us on Twitter: @ChromaticHQ!

If you stuck with us through all 7 Code Standards posts, let us know what you thought! We hope they were helpful for new and experienced coders alike. This is all we have planned in this series - for now - but there's always more great content coming!