Theme API overview

Last updated on
19 December 2016

In Drupal 7 the whole page is built as an array and only gets rendered into HTML — or as an AJAX reply or whatever else the menu entry defines as delivery callback — at the end of the request. This means your page callback (and hook_block_view) should return an array instead of a string. A string is still supported but then you lose a lot of the flexibility and scalability because parts of the page array can be cached, too. For example, in Drupal 6 you would do something like:

function foo_page() {
  $output = '';
  $output .= theme('table', $header, $rows);
  $output .= theme('pager');
  return $output;

It still works in D7, but it's much better to use

function foo_page() {
  $build = array();
  $build['table'] = array(
    '#theme' => 'table',
    '#header' => $header,
    '#rows' => $rows,
  $build['pager'] = array(
    '#theme' => 'pager',
  return $build;

You can then use hook_page_alter() to add a class to the table:

function bar_page_alter(&$build) {
  $build['table']['#attributes']['class'][] = 'bar';

Tip: use the devel module to see what's in the page array.

We have learned a number of important things:

  • Any theme function can be used, just prepend a # before the name of the argument. Argument names — together with their defaults — are defined in hook_theme().
  • The class attribute is now an array, not a string.

All theme calls should now use one array parameter instead of a variable number of unnamed arguments. This allows skipping defaults and simplifying long argument lists as well as unifying all theme handling code.

function foo_theme() {
  return array(
    'foo_greetings' => array(
      'variables' => array(
        'title' => NULL,
        'name' => NULL,

You can then call this with:

theme('foo_greetings', array('title' => 'Mr', 'name' => 'Bond'));

But wait, we just said that you should not actually call theme() and should instead use the build array with #theme as in the previous example. This is because, theme functions still return strings so inside of theme functions we call other theme functions directly.

function theme_foo_whatever($variables) {
  $account = $variables['account'];
  return theme('foo_greetings', array(
    'title' => $account->title,
    'name' => $account->name,

This also shows that all theme functions also receive one argument and you should fish out the actual variables. Note that preprocess always worked like this. This is what "unifying all theme handling code" meant. Also note that while extract helps, it does not improve readability much.

All this magic requires a formerly non-existent distinction between theme functions taking variables and a renderable element (like a form). For example:

function node_theme() {
  return array(
    'node_form' => array(
      'render element' => 'form',
function theme_node_form($variables) {
  $form = $variables['form'];

  $output = "\n

\n"; $output .= "

\n"; $output .= drupal_render_children($form); $output .= "

\n"; $output .= "

\n"; return $output; }

This distinction is necessary because when you work with a form, it has a #theme key but you do not want every form property to be extracted into a theme variable, you want the whole form to be passed to the theme function.

This example has shown a new function called drupal_render_children. This should be your last call in a form rendering function, not drupal_render.

Finally, in your templates you can just print render($foo); to print a renderable array which are now ubiquitous.