Create custom twig templates for custom module
The general idea in Drupal 8 is that you want to avoid creating html directly in the PHP code of your custom module. You want this to go into twig templates. To create new twig templates in your module, go through the following steps.
Step #1: Define hook_theme() in .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 key of each item in the array is what you will need to call the template later. Do not use dashes in the file name.
/**
* Implements hook_theme().
*/
function [module]_theme($existing, $type, $theme, $path) {
return [
'my_template' => [
'variables' => ['test_var' => NULL],
],
];
}
See the documentation for hook_theme().
Step #2: Create Twig Template
In your module, inside of the templates folder, create your twig template. The name of the file has to match what you put into hook_theme()
(make sure replace underscores with dashes). In this case, the file name would be my-template.html.twig
.
Here is the file I used to test:
{# [module]/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.
Step #3.1: 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'),
];
}
}
Step #3.2: 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:
$path = drupal_get_path('module', 'your_module') . '/templates/email-table.html.twig';
$rendered = \Drupal::service('twig')->load($path)->render([
'test_var' => $value
]);
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.
Step #3.3: 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 about above example 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