Skip to main content

Using the new add_suggestion Twig filter in Drupal 10

Published on

When building my wife's web store, I used a new feature in Drupal 10 when templating her product display pages. Drupal 10 introduced a new Twig filter called add_suggestion. Drupal's render array output is templated through Twig and provides a base template name and more refined alternatives for controlled templating. Here's a list of template suggestions for outputting an image field from a product entity, from the most generic to the most specific.

  • field.html.twig: The default for all fields
  • field--entity-reference.html.twig: The default for all entity reference fields.
  • field--field-images.html.twig: The default for fields with this name (across all entity types and bundles.)
  • field--commerce-product.html.twig: The default for all fields of this entity type (commerce_product.)
  • field--commerce-product--cookie-set.html.twig: The default for all fields of this entity type (commerce_product) and bundle (cookie_set.)
  • field--commerce-product--field-images.html.twig: The default for fields with this name for this entity type (commerce_product)
  • field--commerce-product--field-images--cookie-set.html.twig: The default for fields with this name for this entity type (commerce_product) and bundle (cookie_set.)

Many times, developers need to customize the output for a field but not for every instance. In my use case, all products have a media field for images that allows multiple values to create a gallery. However, I only want one image to show when the product is displayed in a list. I remembered Mike Herchel's blog post about the new add_suggestion filter. This was a perfect opportunity to give it a spin!

How template suggestions are provided now

Drupal does provide various theme suggestions out of the box for most templates. The system_theme_suggestions_field implementation provides suggestions for fields based on the entity type, entity bundle, and field name.

/**
 * Implements hook_theme_suggestions_HOOK().
 */
function system_theme_suggestions_field(array $variables) {
  $suggestions = [];
  $element = $variables['element'];

  $suggestions[] = 'field__' . $element['#field_type'];
  $suggestions[] = 'field__' . $element['#field_name'];
  $suggestions[] = 'field__' . $element['#entity_type'] . '__' . $element['#bundle'];
  $suggestions[] = 'field__' . $element['#entity_type'] . '__' . $element['#field_name'];
  $suggestions[] = 'field__' . $element['#entity_type'] . '__' . $element['#field_name'] . '__' . $element['#bundle'];

  return $suggestions;
}

To add a new suggestion, a developer would need to use a module or theme to implement one of the following alters to add additional suggestions.

  • hook_theme_suggestions_HOOK: A hook to add new suggestions for a specific template; for example, replace HOOK with field.
  • hook_theme_suggestions_alter: A hook to alter, such as remove, specific template suggestions.
  • hook_theme_suggestions_HOOK_alter: A hook to alter suggestions for a specific template.

That means the developer needs to understand PHP and how Drupal hooks work. While it's not a steep learning curve for these aspects, it's still an extra barrier. This new add_suggestion filter bypasses the need to implement these hooks to have a specific template used when needed.

Here is the official change record announcing the feature: https://www.drupal.org/node/3301862

How to use add_suggestion

Here is the code for my commerce-product--listing.html.twig Twig template. This is used whenever a product is rendered with its listing view mode.

<article{{ attributes.addClass('group relative') }}>
  {{ title_prefix }}
  {{ title_suffix }}
  <a href="{{ product_url }}"></a>
  {# Use a custom theme suggestion to render the first media image. #}
  {{- product.field_images|add_suggestion('commerce_product__field_images__single') -}}
  <p class="text-lg font-bold">
    <a href="{{ product_url }}">
      <span aria-hidden="true" class="absolute inset-0"></span>
      {{- product.title -}}
    </a>
  </p>
</article>

All suggestions are built off of the original theme hook, which in this case, will be field for all field values. The suggestion argument passed to add_suggestion gets appended to create {$original_theme_hook}__{$suggestion}. Drupal uses two underscores (__) to identify variations in templates. This will cause the field_images field content to add a new theme hook suggestion of commerce_product__field_images__single when rendered. This theme hook suggestion translates to the template name of field--commerce-product--field-images--single.html.twig. I could have simplified my suggestion name to something shorter, like product-single-image. But, I followed patterns that I am used to and their organization.

A quirk with debugging and finding the new template name

I heavily rely on Drupal's theme debugging to see the possible Twig templates that can be used. 

<!-- THEME DEBUG -->
<!-- THEME HOOK: 'field' -->
<!-- FILE NAME SUGGESTIONS:
   * field--commerce-product--field-images--cookie-set.html.twig
   * field--commerce-product--field-images.html.twig
   * field--commerce-product--cookie-set.html.twig
   * field--field-images.html.twig
   * field--entity-reference.html.twig
   x field.html.twig
-->
<!-- BEGIN OUTPUT from 'themes/custom/artsy/templates/field/field.html.twig' -->

Due to this filter running within the Twig templating engine and not Drupal's theming system, the suggestions provided by add_suggestion are not output as debugging comments. I had to review how the Twig filter worked to realize what my template needed to be named. There is a very old Drupal core issue related to this: https://www.drupal.org/project/drupal/issues/2118743

That means some trickery is required to know what your Twig template should be named. The developer will need to know the base theme hook, which can be derived from the list of possible Twig template suggestions.

#