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 fieldsfield--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, replaceHOOK
withfield
.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.
Want more? Sign up for my weekly newsletter