Change record status: 
Introduced in branch: 
Introduced in version: 

The theme system from all previous versions of Drupal of using theme_* functions and PHP-based *.tpl.php files has been completely replaced in Drupal 8. Instead of using theme functions and theme template files, Drupal 8.x is now using the Twig templating system.

Example of converting an existing theme function to a Twig template (theme_datetime() ):
Drupal 8.x before the conversion (it was the same as a Drupal 7 theme function except for the new Attributes mechanism):

function theme_datetime($variables) {
  $output = '<time' . new Attribute($variables['attributes']) . '>';
  $output .= !empty($variables['html']) ? $variables['text'] : check_plain($variables['text']);
  $output .= '</time>';
  return $output;

Drupal 8.x: Twig template (file name: time.html.twig)

 * @file
 * Default theme implementation for a date / time element.
 * Available variables
 * - timestamp: (optional) A UNIX timestamp for the datetime attribute. If the
 *   datetime cannot be represented as a UNIX timestamp, use a valid datetime
 *   attribute value in attributes.datetime.
 * - text: (optional) The content to display within the <time> element.
 *   Defaults to a human-readable representation of the timestamp value or the
 *   datetime attribute value using format_date().
 * - attributes: (optional) HTML attributes to apply to the <time> element.
 *   A datetime attribute in 'attributes' overrides the 'timestamp'. To
 *   create a valid datetime attribute value from a UNIX timestamp, use
 *   format_date() with one of the predefined 'html_*' formats.
 * @see template_preprocess_time()
 * @see
<time{{ attributes }}>{{ text }}</time>

Also compare node.tpl.php to node.html.twig.

- Twig documentation:
- Generic Twig coding standards:
- Twig in Drupal coding standards:

Module developers

There will be one huge change to how you create themable output in Drupal 8. There will be no theme functions. All themable output must be run through a template file. Preferably, one that has already been provided by core (we are currently revising and consolidating all template files in core to make them more re-usable).

You will still be able to call theme('foo', $variables); just like you always have, so hopefully the process of theming your output won't change very much for you. <-- This is no longer true. See change record: theme() renamed to _theme() and should not be called directly

One thing to remember, is that your markup is probably not unique, and it is important not to create a new Twig template for your module unless it is absolutely necessary. A complete list of templates in core will be posted as soon as they are decided on and committed :)

Define Twig extensions

Modules can define twig extensions like in Symfony by adding the following to

For a module called twig_extension_test this would look like the following:

    class: Drupal\twig_extension_test\Twig\TestExtension
      - { name: twig.extension }

The "tags" entry tells the Dependency Injection system to register this class with the other Twig extensions. Note that the 'Twig' part of the namespace is optional, and intended to allow you to grow and add other Twig-related classes as needed.

The class above this will live at modules/twig_extension_test/src/Twig/TestExtension.php and will look something like the following:

namespace Drupal\twig_extension_test\Twig;

class TestExtension extends \Twig_Extension {

   * This string must be unique but there's no special rules on it.
   * The recommended approach is just returning the magic constant __CLASS__
   * since it will always be unique to the class (it contains the fully
   * qualified name of the class which will include the namespace).
  public function getName() {
    return __CLASS__;

   * This function passes over the definitions of your filter functions
   * to twig. This is done with an array with a format of:
   * It's recommended that you include your module name in your Twig filter,
   * function, etc. names to prevent a namespace collision with another module.
   * The function will receive one argument - the variable to the left of the |
   * character in the twig template.
   * For more information, see
  public function getFilters() {
    return [
       * By Closure. This works best with smallish operations.
      new \Twig_SimpleFilter('closure_style', function($var) {
        return 'foo' . strtoupper($var);

       * By static method on *any* class, but usually on this class
      new \Twig_SimpleFilter('static_style', [__CLASS__, 'staticMethod']),

       * Finally to this object, useful if you need to keep some form
       * of internal count of how many times the function was called
       * for some reason.
      new \Twig_SimpleFilter('dynamic_style', [$this, 'objectMethod']),

   * Example of a callback for the static method above.
  public static function staticMethod($var) {
    return $var . 'bar';

   * And object is the same except it doesn't need the static keyword.
  public function objectMethod($var) {
    return 'baa' . $var;


Within Twig these filters would look like this:

  {{ my_variable|closure_style }}
  {{ my_variable|static_style }}
  {{ my_variable|dynamic_style }}

Other ways of extending Twig (functions, tests, tags, etc.) are created as outlined in the Twig documentation.

Site builders, administrators, editors
Module developers
Updates Done (doc team, etc.)
Online documentation: 
Not done
Theming guide: 
Not done
Module developer documentation: 
Not done
Examples project: 
Not done
Coder Review: 
Not done
Coder Upgrade: 
Not done
Other updates done


simg’s picture

but how do modules implement / change over to the twig system? Unless I've missed something, the example shown doesn't really show how to make the change (at all)

jenlampton’s picture

Good point, we'll add that.

simg’s picture

no, thank you :) !

alexmoreno’s picture

yes, i'd love to see how to pass variables from core or from modules to the twig theme. I've used twig in Symfony2, and I love it. It avoid for example the excessive use of php (or use at all) in the view (theme), but i'd like to see how D8 implements that.


Aki Tendo’s picture

After 2 days of code study and searching, I figured out how this is done and updated the documentation above accordingly.

boutrosa’s picture

the 'node.html.twig' link above in the sentence "Also compare node.tpl.php to node.html.twig."
is being redirected to a blank page.
The proper link is: