Drupal 8: How to Craft Custom Theme Hook Suggestions & Templates
I've been theming with Drupal since version 6 back in 2009. In the intervening years, I've seen a lot of changes of how theming and front-end development is approached and have tried to keep up with the latest trends and adhere to best practices. Drupal 8 takes theming to a whole new level; it feels elegant, freeing, uniform, cohesive, and logical. So there's a lot to get excited about here.
In Drupal 8, there's templates for just about anything and with Drupal 8's new theme hooks, you can really narrow in on crafting a custom template for your specific use case. This makes getting at and altering markup far more accessible than it was in Drupal 6 and 7.
My preferred method for Drupal 8 theming is using Classy as my base theme, creating a sub-theme off of that and then overriding the parts that are necessary. An override is as simple as copying one of Classy's 100+ templates into your Classy based sub-theme. Sometimes, an out of the box template is not enough so you can create your own theme hook in that case.
In this post, I'll help you get started with creating Drupal 8 custom theme hook suggestions and templates. I also refer to various tools that I use, you can read more about them in my article, Drupal 8 Development: 5 Things to Add to Your Theming Toolbox.
Overview
The goal for this tutorial is to create a theme hook and subsequent template for a group of block regions in our site. The regions are called postscript_one
, postscript_two
, and postscript_three
. The Drupal API function we will use for this is hook_theme_suggestions_HOOK_alter
where "HOOK" is the name of the theme hook that we see in our Twig debug area. So essentially, we'll grab this theme hook and make a template suggestion for an array of these regions.
Getting Started
To start off, I'll place a block in one of my postscript regions and then examine the placed block with Web Inspector. Here is what I see when we do that:
<div class="postscript">
<!-- THEME DEBUG -->
<!-- THEME HOOK: 'region' -->
<!-- FILE NAME SUGGESTIONS:
* region--postscript-second.html.twig
x region.html.twig
-->
<!-- BEGIN OUTPUT from 'themes/custom/hibiscus/templates/layout/region.html.twig' -->
From the above, we see the theme hook name, region
as well as a two template suggestions. What I am after here is a common region template for any of the the three postscript regions that I mentioned earlier. Time to fire up Devel Kint and see what we can learn by inspecting the region array. For this, I will initiate my theme hook function in my theme's .theme file
. Of note is hibiscus
, my Classy sub-theme theme name and the HOOK
, "region".
/\*\*
- Implements hook_theme_suggestions_HOOK_alter().
\*/
function hibiscus_theme_suggestions_region_alter(&$suggestions, $vars, $hook) {
}
Now I'll enable kint()
as kint($vars);
:
function hibiscus_theme_suggestions_region_alter(&$suggestions, $vars, $hook) {
kint($vars);
}
After I run drupal cache:rebuild all
using Drupal Console, I'll now see kint print out the region array on my page. As you can see from the below image.
Construct the theme hook
Using Kint search, we can search for and copy the path for the region name definition, $var['elements']['#region']
It is from this that we will construct our array of regions and subsequent theme hook. First I define the region name variable from the above:
$region = $vars['elements']['#region'];
Now I'll define an array of my three regions:
$region_postscript = array(
'postscript_first',
'postscript_second',
'postscript_third',
);
Next I construct an if statement to make the actual theme suggestion:
if (in_array($region, $region_postscript)) {
$suggestions[] = 'region' . '\_\_' . 'postscript';
}
What this does is say, if it's any of the three postscript regions, create a theme hook that's common to all these regions.
Putting it all together
Putting the entire function all together, we now have:
/\*\*
- Implements hook_theme_suggestions_HOOK_alter().
\*/
function hibiscus_theme_suggestions_region_alter(&$suggestions, $vars, $hook) {
// Define a variable for the #region name.
$region = $vars['elements']['#region'];
// Define an array of our postscript regions.
$region_postscript = array(
'postscript_first',
'postscript_second',
'postscript_third',
);
// If we are in any of these regions, create a theme hook suggestion.
if (in_array($region, $region_postscript)) {
$suggestions[] = 'region' . '\_\_' . 'postscript';
}
}
Now that I have written my theme hook, I can clear cache and reload the page to see my new theme hook suggestion in action.
<!-- FILE NAME SUGGESTIONS:
* region--postscript.html.twig
* region--postscript-second.html.twig
x region.html.twig
We now see out new template suggestion from our custom theme hook, region--postscript.html.twig
. This template would be common to all three regions so we can alter or update the markup in this new template as well as add classes as we see fit. Since I have a Classy sub-theme in my case, I copy Classy's region.html.twig and rename it to the new name within my own sub-theme folder structure. Of course if you are using stable as your base theme, you would do the same thing copying stable's template of the same name.
Summary
This is just a basic example to show what's possible with creating Drupal 8 theme hooks and template suggestions. Hopefully you can see that it's really useful and should come in handy for themers.
Resources
- Drupal 8 theme system overview
- function hook_theme _suggestions_HOOK_alter
- Drupal 8 Theming: Setting up theme debugging
- Devel / Kint
- Search Kint
- Drupal Console