Using template files in your own module

Last updated on
12 March 2021

Drupal 7 will no longer be supported after January 5, 2025. Learn more and find resources for Drupal 7 sites

This documentation needs work. See "Help improve this page" in the sidebar.

Normally template files reside in the theme folder.

But sometimes custom modules may need to override the default template files -- page, node, etc. -- provided by Drupal. It can be tricky figuring out how to do this, so this page will present approaches for Drupal 5, 6, 7 and 8.

Why would you want to override a template file in a module rather than in the theme?
There are a number of reasons a module would need to have control of how a piece of content is presented. A module may provide a new content type with a custom node template. In the case of the Print module, it is necessary to exclude the header, footer, and sidebars and then add some other elements.

The Drupal 6 and 7 approaches are similar: telling the theme system to look in the module directory for templates as well as the theme directory. In both cases, it may be necessary to limit when the custom template is used.

I.e., if a module has its own page.tpl.php, it probably should not be used for every page on the site.

This can be done by naming the custom template in such a way that it will only be used in certain cases. 

For finer control, it can also be done using a preprocess function.

Refer to the following pages for additional documentation on template suggestions.

Drupal 8

This is done by using HOOK_theme() to register the template.

/**
 * Implements hook_theme().
 */
function MODULE_theme() {
  $theme['paragraph__table'] = [
    'base hook' => 'paragraph',
  ];

  return $theme;
}

This can be coupled with hook_theme_suggestions_HOOK() to provide unique template suggestions.

/**
 * Implements hook_theme_suggestions_HOOK().
 */

function MODULE_theme_suggestions_paragraph(array $variables) {
  $suggestions = [];

  $suggestions[] = 'paragraph__table';

  return $suggestions;
}

Then copy the corresponding TWIG template into your modules templates directory and rename to match your template suggestion name, replacing any underscores with hyphens.

i.e. From this example, the file would be /templates/paragraph--table.html.twig

Drupal 7

The Drupal 7 approach is adapted from Adding a module path to the Drupal 7 theme registry.

First, create a template file in your module directory. The file requires a double hyphen:

node--mymodule.tpl.php

The next step is to tell the theme system to look there.

/**
 * Implements hook_theme_registry_alter().
 */
function my_module_theme_registry_alter(&$theme_registry) {
  // Defined path to the current module.
  $module_path = drupal_get_path('module', 'my_module');
  // Find all .tpl.php files in this module's folder recursively.
  $template_file_objects = drupal_find_theme_templates($theme_registry, '.tpl.php', $module_path);
  // Iterate through all found template file objects.
  foreach ($template_file_objects as $key => $template_file_object) {
    // If the template has not already been overridden by a theme.
    if (!isset($theme_registry[$key]['theme path']) || !preg_match('#/themes/#', $theme_registry[$key]['theme path'])) {
      // Alter the theme path and template elements.
      $theme_registry[$key]['theme path'] = $module_path;
      $theme_registry[$key] = array_merge($theme_registry[$key], $template_file_object);
      $theme_registry[$key]['type'] = 'module';
    }
  }
}

Templates for content types in a custom module

As an alternative to the solution presented above it is possible to implement hook_theme to add templates for content types to the custom module.

    /**
     * Implements hook_theme().
     */
    function mymodule_theme($existing, $type, $theme, $path) {
      $theme = array();
      $theme['node__blog_post'] = array(
        'render element' => 'content',
        'base hook' => 'node',
        'template' => 'node--blog_post',
        'path' => drupal_get_path('module', 'mymodule') . '/templates',
       );
      return $theme;
    }

Notes: the custom module here is called 'mymodule' and the custom content type is called 'blog_post'. The tpl.php that is used is called 'node--blog_post.tpl.php' and it is located in the 'templates' subfolder of the custom module.

Drupal 6

The Drupal 6 technique comes from Theming Drupal 6 from the Module Layer.

First, create a template file in your module directory.

Then tell the theme system to look there.

// Create a variable to store the path to this module
define('MY_MODULE_PATH', drupal_get_path('module', 'my_module'));

function special_page_theme_registry_alter(&$theme_registry) {
  // A list of templates the module will provide templates for
  $hooks = array('page');

  foreach ($hooks as $h) {
    // Add the module path on top in the array of paths
    array_unshift($theme_registry[$h]['theme paths'], MY_MODULE_PATH);
  }
}

Drupal 5

For Drupal 5 I will describe a workaround inspired by the Print module.

Overview of the solution

1. Set up the variables that will be used in the page template.
2. Create a new page template, mypage.tpl.php, inside your module.
3. Include the page template.

Details of the solution

1. Since this is a workaround and not part of the theme system, your template will not have access to the regular theme variables. You will have to set the variables that you want the new template to have access to:

function _mymodule_print_vars_setup() {
    $print['language'] = $GLOBALS['locale'];
    $print['title'] = 'Page XYZ';
    $print['head'] = drupal_get_html_head();
    $print['favicon'] = theme_get_setting("toggle_favicon") ? "\n" : "";

    $css_files = array(drupal_get_path('module','mymodule').'/stylesheet1.css', drupal_get_path('module','mymodule').'/stylesheet2.css');
    foreach ($css_files as $css_file)
        $print["css"] .= "@import \"". base_path() . $css_file['file'] ."\";\n";

    $print["content"] = _mymodule_page_content();

    return $print;
}

}

2. Create a template file, which will be very similar to the theme's page.tpl.php, except for the variables you will use, which will be from the $print array you created in step 1.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="<?php print $print['language'] ?>" lang="<?php print $print['language'] ?>" >
  <head>
    <title><?php print $print['title'] ?></title>
    <?php print $print['head'] ?>
    <?php print $print["favicon"] ?>
    <?php print $print['css'] ?>
  </head>
  <body>
    <div id="wrapper">
        <div id="main-content">
	    <?php print $print['content']; ?>
        </div>
        <div id="right-bar">
          <?php print theme('blocks', 'right_bar'); ?>
        </div>
      </div>
    </div>
  </body>
</html>

You can access your main theme's regions using theme_blocks, as shown for the right bar.

3. Then create a function that will be the callback to one of the items in your modules hook_menu implementation:

function mymodule_special_page() {
    $print = _mymodule_print_vars_setup();
    include_once(drupal_get_path('module','mymodule').'/mypage.tpl.php');
}

For a complete example of this method, study the Print module.

Help improve this page

Page status: Needs work

You can: