Score:2

How do you override a module's template from another module?

ph flag

I'm trying to override an email template of the webform module. The project is headless so we don't have a custom theme, and we'd like to stick with the Adminimal contrib theme. However, we do have a custom module named app in web/modules/app.

Here are the default template suggestions shown in the email source:

<!-- THEME DEBUG -->
<!-- THEME HOOK: 'webform_email_html' -->
<!-- FILE NAME SUGGESTIONS:
   * webform-email-html--contact--email--email-notification.html.twig
   * webform-email-html--contact--email.html.twig
   * webform-email-html--contact.html.twig
   x webform-email-html.html.twig
-->
<!-- BEGIN OUTPUT from 'modules/contrib/webform/templates/webform-email-html.html.twig' -->

If I just create a web/modules/app/templates/webform-email-html.html.twig file (or any of the other suggestions), the template override doesn't work: the template of the webform module is used.

Score:2
ph flag

The only way I found to get template override to work is to:

  1. add a template suggestion:
    // in web/modules/app/app.module
    function app_theme_suggestions_alter(array &$suggestions, array $variables, $hook) {
      $suggestions[] = $hook . '--' . 'custom';
    }
    
  2. force the template used to render the webform_email_html--custom template suggestion:
    // in web/modules/app/app.module
    function app_theme($existing, $type, $theme, $path) {
      return [
        'webform_email_html--custom' => [
          'template' => 'webform-email-html--custom',
        ],
      ];
    }
    
  3. create the web/modules/app/templates/webform-email-html.html.twig file

I'm not marking this answer as accepted on purpose: please tell me there's a better way, this feels overly complicated! There doesn't seem to have a better way…

ru flag
This is working as intented. Themes override module templates out of the box, module do not override templates. (And should not do this out of the box to avoid collisions. There is only one active theme, but potentially hunders of active modules.)
4uk4 avatar
cn flag
Please tell me there's a better way ... There is not, and this is pretty common, see for example the core comment module overriding a template of the field module which then can be overridden again by a theme. But you need to append `__custom` instead of `--custom` and define the base hook instead of the template name `'base hook' => 'webform_email_html',`. You need the base hook to include the preprocess handlers of the base template and you don't need the template name because this is already defined by the hook name.
Score:0
mn flag

Thanks for this! I wanted to clean up the answer a little bit. This is essentially the same with a couple changes:

  1. Instead of creating a new theme suggestion/hook for every hook already in the system, we create one only for the hook we want to create a new template for. This is a much less heavy handed approach.
  2. As 4uk4 pointed out, dashes have been replaced with underscores.
  3. As 4uk4 pointed out, we use base hook instead of template in our hook_theme() implementation.
  4. We add path in our hook_theme() implementation, allowing us to sort templates within our module. In this case we put the template in templates/webform.
// In web/modules/app/app.module

/**
 * Implements hook_theme_suggestions_alter().
 */
function app_theme_suggestions_alter(array &$suggestions, array $variables, $hook) {
  // Use switch so we can easily add another hook in the future.
  switch ($hook) {
    case 'webform_email_html':
      $suggestions[] = $hook . '__' . 'custom';
      break;
  }
}
// In web/modules/app/app.module

/**
 * Implements hook_theme().
 */
function app_theme($existing, $type, $theme, $path) {
  return [
    'webform_email_html__custom' => [
      'base hook' => 'webform_email_html',
      'path' => $path . '/templates/webform',
    ],
  ];
}
I sit in a Tesla and translated this thread with Ai:

mangohost

Post an answer

Most people don’t grasp that asking a lot of questions unlocks learning and improves interpersonal bonding. In Alison’s studies, for example, though people could accurately recall how many questions had been asked in their conversations, they didn’t intuit the link between questions and liking. Across four studies, in which participants were engaged in conversations themselves or read transcripts of others’ conversations, people tended not to realize that question asking would influence—or had influenced—the level of amity between the conversationalists.