Create custom Twig templates for a custom module
This documentation needs review. See "Help improve this page" in the sidebar.
HTML should not be created in PHP in Drupal. Rather, HTML should be generated in twig templates. Creation of a templates in a module to render HTML is handled as follows:
Step #1: Define hook_theme() in a .module file
Create a [module].module file, if it doesn't already exist in your module, and add code that defines each of your twig templates. The keys in the returned array defined theme hooks; it's the base name for the theme, from which template suggestions and template names are defined. Theme hooks must NOT contain hyphens (use underscores).
/**
* Implements hook_theme().
*/
function [module]_theme($existing, $type, $theme, $path) {
return [
'my_template' => [
'variables' => ['test_var' => NULL],
],
];
}
The above implementation declares the theme hook, my_template that will be rendered with the template file my-template.html.twig.
See the documentation for hook_theme().
Step #2: Create the Twig template
Default files MUST be created in a module for any theme hooks declared in step one in hook_theme(). The template files MUST be created in the template directory of the module; [MODULE ROOT]/templates/[TEMPLATE]. The template file name will be the theme hook, with any underscores replaced by hyphens, then .html.twig is appended. So when declaring theme hook my_template, the template file must be created at [MODULE ROOT]/templates/my-template.html.twig:
{# [MODULE ROOT]/templates/my-template.html.twig #}
<p>Test twig template!</p>
<p>test_var: {{ test_var }}</p>
The beauty of this is that the template file defined in your module will be used if such a file does not already exist in your theme. Just dump a file inside of the templates folder of your theme, clear cache (drush cache-rebuild/ drush cr), and it will read that file instead.
You can put the file in any nested sub-folder of the site theme to keep things organized.
Step #3: Call the template
The usage examples below with 3 different scenarios. Use the example that suits your use case.
Call from controller
In the place where you are returning your render array (whether from a controller method that is called from your router yml file, or wherever), make a call to your twig template. Below is an example from a testing module that is called from the routing yml file in the module. (need more info on this part)
<?php
/**
* @file
* Contains \Drupal\test_twig\Controller\TestTwigController.
*/
namespace Drupal\[module_name]\Controller;
use Drupal\Core\Controller\ControllerBase;
class TestTwigController extends ControllerBase {
public function content() {
return [
'#theme' => 'my_template',
'#test_var' => $this->t('Test Value'),
];
}
}
Render as HTML
You can also use render service method to build the output if you need to use this as part of a different workflow in your code: -
$renderable = [
'#theme' => 'my_template',
'#test_var' => 'test variable',
];
$rendered = \Drupal::service('renderer')->renderPlain($renderable);
Or if using in a not standard place such as email template you can:
use Drupal\Core\Render\Markup;
$modulePath = \Drupal::service('extension.path.resolver')->getPath('module', 'my_module');
$path = $modulePath . '/templates/email-table.html.twig';
$rendered = \Drupal::service('twig')->load($path)->render([
'test_var' => $value
]);
$variables['email_template'] = Markup::create($rendered);Keep in mind that this is a basic implementation, and doesn't do any kind of caching. The Render API Overview docs contain more information about how you can add caching to this. Speaking of caching - variable names will be cached and if you change them (say, "test_var" to "my_var") you'll have to refresh the cache.
Render as part of another plugin (such as block)
You can also use render array as output of custom plugin such as block:
<?php
namespace Drupal\[module_name]\Plugin\Block;
use Drupal\Core\Block\BlockBase;
/**
* Provides a 'My Template' block.
*
* @Block(
* id = "my_template_block",
* admin_label = @Translation("My Template")
* )
*/
class MyTemplateBlock extends BlockBase {
/**
* {@inheritdoc}
*/
public function defaultConfiguration() {
return ['label_display' => FALSE];
}
/**
* {@inheritdoc}
*/
public function build() {
$renderable = [
'#theme' => 'my_template',
'#test_var' => 'test variable',
];
return $renderable;
}
}
For more information see Block API overview.
Help improve this page
You can:
- Log in, click Edit, and edit this page
- Log in, click Discuss, update the Page status value, and suggest an improvement
- Log in and create a Documentation issue with your suggestion