---
title: "Tip: Using #pre_render in Multistep Forms"
url: "/articles/tip-using-pre_render-in-multistep-forms"
type: article
date: 2011-05-05
updated: 2019-01-11
---

# Tip: Using #pre_render in Multistep Forms

#  Tip: Using #pre\_render in Multistep Forms 

 The Form API in Drupal is a complex and powerful system that touches nearly  
 every page in a Drupal site

 By

 [  Andrew Berry   ](/about/andrew-berry) 

 May 5, 2011

  

The Form API in Drupal is a complex and powerful system that touches nearly every page in a Drupal site. Forms can be as simple as the search or login blocks commonly used. Or, they can be complex forms of interaction using [\#ahah](https://drupalcode.org/project/examples.git/tree/refs/heads/6.x-1.x:/ahah_example), [jQuery](http://drupal.org/node/171213), and [multiple steps](https://drupalcode.org/project/examples.git/tree/refs/heads/6.x-1.x:/form_example) to gather complex information while providing a usable and unique user experience. A common requirement for a multistep form is to have a different page title for each step of the form. This allows users to know what step they are on during a multistep process. In this article, we'll examine how FormAPI's #pre\_render functions can make that happen. A normal multistep form implementation will look something like this: In `site_join.module`:

```php

/**
 * Implementation of hook_menu().
 */
function site_join_menu() {
  $items = array();

  $items['join/%membership_type'] = array(
    'title' => 'Apply for a Membership',
    'description' => 'Application form for new memberships',
    'page callback' => 'drupal_get_form',
    'page arguments' => array('site_join_application', 1),
    'access callback' => 'site_join_application_access',
    'access arguments' => array(1),
    'file' => 'site_join.application.inc',
    'file path' => drupal_get_path('module', 'site_join') . '/includes',
    'type' => MENU_CALLBACK,
  );

  return $items;
}

```

In `includes/site_join.application.inc`:

```php

/**
 * Form API callback for the membership form.
 *
 * @param $form_state
 *   The current state of the form.
 * @param $membership_type
 *   The type of membership.
 */
function site_join_application($form_state, $membership_type) {
  if (!empty($form_state['storage']['step'])) {
    // We are beyond the first step of the form.
    $form = $form_state['storage']['step']['callback'](#);
  }
  else {
    // Start the form.
    $form = site_join_application_member_validation($form_state, $membership_type);
  }
  return $form;
}

/**
 * Form API callback for the member validation step of the application form.
 *
 * @param $form_state
 *   The current state of the form.
 * @param $membership_type
 *   The type of membership.
 *
 * @return
 *   A form API array.
 */
function site_join_application_member_validation(&$form_state, $membership_type) {
  // If we are on this form step, set the page title.
  drupal_set_title(t("Validate your membership"));

  $form = array();
  // Build your form in $form here.
  return $form;
}

```

This works really well, until you want to start reusing the form generation code as a smaller part of another form. Each form function needs to know if it should set the page title or not. So, we add a third parameter to our form function:

```php

/**
 * Form API callback for the membership form.
 *
 * @param $form_state
 *   The current state of the form.
 * @param $membership_type
 *   The type of membership.
 */
function site_join_application($form_state, $membership_type) {
  if (!empty($form_state['storage']['step'])) {
    // We are beyond the first step of the form.
    // The form determines if the step should set the page title by
    // setting 'set_page_title' to TRUE.
    $form = $form_state['storage']['step']['callback'](#);
  }
  else {
    // Start the form.
    $form = site_join_application_member_validation($form_state, $membership_type, TRUE);
  }
  return $form;
}

```

```php

/**
 * Form API callback for the member validation step of the application form.
 *
 * @param $form_state
 *   The current state of the form.
 * @param $membership_type
 *   The type of membership.
 * @param $set_page_title
 *   Optional parameter to indicate that this form is the "primary" form for
 *   the page and should set the page title.
 *
 * @return
 *   A form API array.
 */
function site_join_application_member_validation(&$form_state, $membership_type, $set_page_title = FALSE) {
  if ($set_page_title) {
    drupal_set_title(t("Validate your membership"));
  }
  // The rest of your form function goes here.
}

```

Running this code will initially look to work fine. Unfortunately, it will break when your form throws a validation error. Drupal caches the output of form functions and uses the cached output when rebuilding a form that has failed validation. This means that `site_join_application()` is never called, and `drupal_set_title()` never gets a chance to override the page title on the rebuilt form.

## \#pre\_render to the rescue!

Luckily, Drupal provides a few FAPI properties that will get called every time a form is built. One of them is [`#pre_render`](http://api.drupal.org/api/drupal/developer--topics--forms_api_reference.html/6#pre_render). `#pre_render` is called every time before an element is rendered with [`drupal_render()`](http://api.drupal.org/api/drupal/includes--common.inc/function/drupal_render/6). Using a `#pre_render` callback, we can ensure that the page title is set when needed for any page.

```php

/**
 * Form API callback for the member validation step of the application form.
 *
 * @param $form_state
 *   The current state of the form.
 * @param $membership_type
 *   The type of membership.
 * @param $set_page_title
 *   Optional parameter to indicate that this form is the "primary" form for
 *   the page and should set the page title.
 *
 * @return
 *   A form API array.
 */
function site_join_application_member_validation(&$form_state, $membership_type, $set_page_title = FALSE) {
  if ($set_page_title) {
    $form['page_title'] = array(
      '#type' => 'value',
      '#value' => t('Validate your membership'),
      '#pre_render' => array('site_join_form_page_title'),
    );
  }
  // The rest of your form function goes here.
}

/**
 * FAPI #pre_render callback to set a page title.
 *
 * We have to do this for multistep forms as when a field fails validation, the
 * form is pulled from the form cache. This means that we never get a chance to
 * call drupal_set_title(). We also can't do it from hook_menu()'s title
 * callback as hook_menu() doesn't know anything about the state of the form.
 *
 * @param $element
 *   The FAPI element who's value contains the page title to set.
 * @return
 *   The modified element that was passed in.
 */
function site_join_form_page_title($element) {
  drupal_set_title($element['#value']);
  return $element;
}

```

Even though the element hasn't been modified, remember to return it as the function calling `#pre_render` expects it. With this method, we can easily set the page title to any value by creating a #value element and setting its #pre\_render to the `site_join_form_page_title()` function. Although it's easy to miss, FormAPI's #pre\_render functions can be a powerful weapon in your page-tweaking arsenal.

 Published in:

- [  Drupal Development   ](/topics/drupal-development)

 

   You must have JavaScript enabled to use this form.

## Get in touch with us

### Tell us about your project. We'd love to hear from you!

 First Name  

 Last Name  

 Your email address  

 Your organization  

 How you heard about us  

 Tell us more 

     Leave this field blank