Twig
Twig is the PHP template engine behind Drupal themes. Files ending in .html.twig turn Drupal's data into HTML and escape output by default.
Twig is the template engine Drupal uses to build HTML. Theme and module template files end in .html.twig and mix ordinary HTML with Twig tags that print variables, repeat items and make decisions. Twig is a PHP library from the Symfony project, and Drupal 11 uses its 3.x version.
How Twig works in Drupal 11
Twig has three kinds of delimiters. {{ ... }} prints the result of an expression, such as a variable. {% ... %} runs a statement, such as a condition or a loop. {# ... #} is a comment. A pipe applies a filter to a value, and filters can be chained.
The variables in a template come from Drupal. A render array describes what to show, and preprocess functions prepare the variables before the template runs. Drupal's guidance is that preprocess functions return render arrays rather than rendered output, and that HTML should ideally come from templates, not from PHP code.
Drupal's own filters and functions
Drupal extends Twig. Filters worth knowing:
t(ortrans) passes a fixed string through Drupal's translation system. Pass only static text, never a variable.withoutprints a render array minus the children you name.renderturns a render array into markup so other filters can work on it.clean_classandclean_idturn a string into a valid class name or ID.safe_joinjoins strings with a separator, andformat_dateformats a timestamp.add_classandset_attribute, added in Drupal 10.1, change a field's classes and attributes.
Useful functions include attach_library(), which loads a CSS or JavaScript library for the template, path() and url() for links to routes, link(), file_url(), create_attribute() and active_theme().
Template names and overrides
Drupal picks templates by name. To override one, place a template with the same or a more specific name in your theme. Underscores in machine names become hyphens, and the parts of a name are joined by double hyphens. For a node, Drupal looks for the most specific file first: node--article--teaser.html.twig, then node--article.html.twig, then node--teaser.html.twig, then node.html.twig. After adding a template, rebuild the cache so Drupal finds it.
Escaping
Twig escapes printed values by default, so a string with HTML tags shows as text unless it is marked safe. Strings returned by t() or by sanitizing helpers such as Html::escape() are marked safe, so Twig does not escape them again. The raw filter disables escaping. Always quote HTML attributes, as in class="{{ class }}".
Example
A small template fragment, as it might appear in a theme's node template:
{{ attach_library('my_theme/node-teaser') }}
{# Print the body fields but leave out the links. #}
<article class="teaser">
{{ content|without('links') }}
<p>{{ 'Read more'|t }}</p>
</article>
To see which variables a template can use, turn on Twig debugging and add {{ dump() }}. The dump() function prints nothing unless debugging is on. The Devel module adds its own devel_dump() function.
Common mistakes
- Using
|rawon user input. It removes the protection Twig gives you. - Leaving Twig debugging on in production. It adds comments to the HTML and recompiles templates when files change. Drupal's guidance is to set the debug options in a development file such as
development.services.yml, not in the main services file, so they cannot reach production by accident. - Forgetting to clear the cache after adding a template. Drupal does not see a new file until the cache is rebuilt.
- Building markup in PHP. Keep HTML in the template and data in the preprocess function.
- Passing a variable to
|t. The filter is for static strings that can be translated once. - Unquoted attributes. Escaping assumes the value sits in quotes.
For marketing and content teams
Editors rarely touch Twig, but it decides the markup around the content they enter. If a headline looks different on a teaser than on the full page, one possible cause is a separate template for that view mode. Asking a developer for "an override for articles in teaser view" is a request they can map directly to a file such as node--article--teaser.html.twig. A new template file also needs a cache rebuild before it takes effect, so allow for that when you ask for a change.
Sources
- Twig documentation, Symfony
- Twig templates reference: delimiters and filters
- Drupal.org: Twig filters in Drupal
- Drupal.org: Functions in Twig templates
- Drupal.org: Twig template naming conventions
- Drupal.org: Debugging Twig templates
- Drupal.org: Writing secure code for Drupal
- Drupal.org: Twig best practices, preprocess functions and templates