Last updated April 9, 2012. Created by chx on February 22, 2010.
Edited by shamio, jdelaune, JimmyAx, sirkitree. Log in to edit this page.

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:

<?php
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

<?php
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:

<?php
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 perpend 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.

<?php
function foo_theme() {
  return array(
   
'foo_greetings' => array(
     
'variables' => array(
       
'title' => NULL,
       
'name' => NULL,
      ),
   );
}
?>

You can then call this with:
<?php
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.
<?php
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:

<?php
function node_theme() {
  return array(
   
'node_form' => array(
     
'render element' => 'form',
    ),
  );
}
function
theme_node_form($variables) {
 
$form = $variables['form'];
 
$output = "\n<div class="node-form">\n";
 
$output .= "  <div class="standard">\n";
 
$output .= drupal_render_children($form);
 
$output .= "  </div>\n";
 
$output .= "</div>\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.

Looking for support? Visit the Drupal.org forums, or join #drupal-support in IRC.