How to Create Custom Theme Suggestions in Drupal 8

Recently a team-mate needed to theme a Views exposed filter element from a <select> list into a series of <checkboxes>. Luckily, with the introduction of Twig and the ever-so-useful Theme Debug setting, theming this has become an easy task. Twig allows us to create very easy-to-understand templates very quickly, and the Theme Debug setting allows us to see what template suggestions and hooks are used for the site elements. Additionally, we’re able to create our own custom theme suggestions using hook_theme_suggestions_HOOK_alter().

Drupal 8 Custom Theme Suggestions

However, not all default hook suggestions are as robust as we might need them to be out-of-the-box. One-off elements and generally more miniscule elements have very basic theme suggestions and don’t always provide more than the single default template. In this case, the “select” element only has the “select.html.twig” template suggestion.

Editing the “select.html.twig” file to meet the needs of the project will change all exposed select-boxes on the site. So, we need a custom template suggestion for just this specific Views exposed filter.

To work through this exercise, you’ll need a View that has an exposed filter of some sort, set to “Dropdown” (a select list). In this example we’ve created a View that shows all Article nodes and provides a dropdown select list of the “Tags” taxonomy terms. We’ll also be using a theme named “Silo” - you can adjust these steps to the name of your theme.

Welcome hook_theme_suggestions_HOOK_alter

The hook_theme_suggestions_HOOK_alter is a pre-process function that allows us to easily produce custom template suggestions based on an existing hook. For instance, if we wanted to alter the theme suggestions for all of our Pages, then we could use silo_theme_suggestions_page_alter(). We can leverage a $suggestions[] array to produce a number of template suggestions for whichever element we specify in the function’s title. In order to edit the <select> element in our “Silo” theme, we want to modify the function to the following:

function silo_theme_suggestions_select_alter(&$suggestions, &$vars) {
  // suggestions go here!
}

Our goal is to use this function to grab the View’s ID, and use that ID as part of a template file. Ideally, this would look something like “select--VIEW_ID_HERE.html.twig”. In order to check on whether or not the current select element is part of a View, we’re going to load in the "currently active route match object" by using Drupal::routeMatch.

In Drupal 8 you can use Route object to get information of whatever Page, Node, View, Term or other element you're viewing. The Drupal::routeMatch method allows us to grab the Route object for use in our functions - this is a very handy method.

Devel and Kint()

This is where the Devel module, and it’s kint() function come in very handy. Using Devel, we can kint() the request object and pick the section with the View as needed. Install and enable the Devel module (with Kint), and let’s add the following line to the hook_theme_suggestions_HOOK_alter() function:

function silo_theme_suggestions_select_alter(&$suggestions, &$vars) {
  $request = \Drupal::routeMatch();
  kint($request);
}

This will print an item like the following towards the top of your View’s page:

Drupal 8 Theme Suggestions

This is a Kint element – if you’re already familiar with Devel, think of this as your new dpm(). Click on the teal bar and you’ll be presented with several bits of information related to this object. Note the different tabs, too!

Drupal 8 Theme Suggestions

Overall this doesn't yet look very helpful, but don't fret - as mentioned before this is a very handy method. We’ll want to dive into the “Available methods” section and leverage the "getRouteObject()" method. This method will allow us to load the Route object.

Drupal 8 Theme Suggestions

You’ll notice 'getRouteObject()' is in the “Available methods” tab. This tab lists the various procedures you can run against the object. Let's apply the getRouteObject() method to our request object, like so:

function silo_theme_suggestions_select_alter(&$suggestions, &$vars, $hook) {
  $request = \Drupal::routeMatch()->getRouteObject();
  kint($request);
}

A page-refresh should reveal the following information:

Drupal 8 Theme Suggestions

Look at all the goodies we have to work with now. Under "defaults" you'll see the 'view_id'. We can certainly use this for our template suggestions, however, it's currently a "private" object item. We'll need to use a method to make it available to our function. Check out the "Available methods" tab and check out all the goodies we have here, too! We'll be using the 'getDefault()' method to find the 'view_id':

function silo_theme_suggestions_select_alter(&$suggestions, &$vars, $hook) {
  $request = \Drupal::routeMatch()->getRouteObject()->getDefault('view_id');
  kint($request);
}

The above should produce a kint() out like so:

Drupal 8 Theme Suggestions

Success!

Now for the fun part

By amending an item to the $suggestions array, we can create our own custom suggestions. Update the function by adding a condition for producing a theme suggestion based on the $request variable:

function silo_theme_suggestions_select_alter(&$suggestions, &$vars, $hook) {
  $request = \Drupal::routeMatch()->getRouteObject()->getDefault('view_id');
  if (!empty($request)) {
    $suggestions[] = 'select__' . $request;
  }
}

This will tell Drupal 8 to look for a 'select--VIEW_ID_HERE.html.twig' file when a View is present on the currently viewed-page, like so:

In the above screenshot you’ll note that the select hook is looking for the select--example-select.html.twig file - success!

You’re welcome to add any number of items to the $suggestions[] array - this can be especially powerful with programmatic information like the View’s ID and the Display ID. If you’re working with a different hook, you can pull out different information like a Node’s view-mode or a Field’s parent node type.