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.

Learn DrupalDrupal glossary

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 (or trans) passes a fixed string through Drupal's translation system. Pass only static text, never a variable.
  • without prints a render array minus the children you name.
  • render turns a render array into markup so other filters can work on it.
  • clean_class and clean_id turn a string into a valid class name or ID.
  • safe_join joins strings with a separator, and format_date formats a timestamp.
  • add_class and set_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 |raw on 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

Questions about Twig in Drupal

Drupal 11 uses Twig 3.x. Drupal adds its own filters and functions on top of the ones Twig provides, so a Drupal template can use names, such as t and attach_library, that plain Twig does not know.

Turn on Twig debugging, either with the setting at /admin/config/development/settings (Drupal 10.1 and later) or with debug: true under twig.config in development.services.yml. The page source then shows comments listing the template suggestions and the file that was used.

Twig escapes whatever you print with {{ }} by default, which protects against many injection mistakes. The raw filter switches that off, so only use it on data you trust, never on text a visitor typed.

Drupal has to discover the file. After adding a template to your theme, rebuild the cache, for example with drush cr. Also check the file name: the pattern uses double hyphens, so the suggestion node__article becomes node--article.html.twig.

Related terms

All Drupal terms from A to Z