4.7.x form.inc drupal_get_form($form_id, &$form, $callback = NULL)
5.x form.inc drupal_get_form($form_id)
6.x form.inc drupal_get_form($form_id)
7.x form.inc drupal_get_form($form_id)

Retrieves a form from a constructor function, or from the cache if the form was built in a previous page-load. The form is then passed on for processing, after and rendered for display if necessary.


$form_id: The unique string identifying the desired form. If a function with that name exists, it is called to build the form array. Modules that need to generate the same form (or very similar forms) using different $form_ids can implement hook_forms(), which maps different $form_id values to the proper form constructor function. Examples may be found in node_forms(), search_forms(), and user_forms().

...: Any additional arguments are passed on to the functions called by drupal_get_form(), including the unique form constructor function. For example, the node_edit form requires that a node object be passed in here when it is called. These are available to implementations of hook_form_alter() and hook_form_FORM_ID_alter() as the array $form['#parameters'].

Return value

The rendered form.

Related topics

40 calls to drupal_get_form()
block_admin_display in modules/block/block.admin.inc
Menu callback for admin/build/block.
book_outline in modules/book/book.pages.inc
Menu callback; show the outline form for a single node.
comment_admin in modules/comment/comment.admin.inc
Menu callback; present an administrative comment listing.
comment_delete in modules/comment/comment.admin.inc
Menu callback; delete a comment.
comment_form_box in modules/comment/comment.module
Theme the comment form box.

... See full list

24 string references to 'drupal_get_form'
aggregator_menu in modules/aggregator/aggregator.module
Implementation of hook_menu().
block_menu in modules/block/block.module
Implementation of hook_menu().
blogapi_menu in modules/blogapi/blogapi.module
book_menu in modules/book/book.module
Implementation of hook_menu().
contact_menu in modules/contact/contact.module
Implementation of hook_menu().

... See full list


includes/form.inc, line 70


function drupal_get_form($form_id) {
  $form_state = array(
    'storage' => NULL,
    'submitted' => FALSE,
  $args = func_get_args();
  $cacheable = FALSE;
  if (isset($_SESSION['batch_form_state'])) {

    // We've been redirected here after a batch processing : the form has
    // already been processed, so we grab the post-process $form_state value
    // and move on to form display. See _batch_finished() function.
    $form_state = $_SESSION['batch_form_state'];
  else {

    // If the incoming $_POST contains a form_build_id, we'll check the
    // cache for a copy of the form in question. If it's there, we don't
    // have to rebuild the form to proceed. In addition, if there is stored
    // form_state data from a previous step, we'll retrieve it so it can
    // be passed on to the form processing code.
    if (isset($_POST['form_id']) && $_POST['form_id'] == $form_id && !empty($_POST['form_build_id'])) {
      $form = form_get_cache($_POST['form_build_id'], $form_state);

    // If the previous bit of code didn't result in a populated $form
    // object, we're hitting the form for the first time and we need
    // to build it from scratch.
    if (!isset($form)) {
      $form_state['post'] = $_POST;

      // Use a copy of the function's arguments for manipulation
      $args_temp = $args;
      $args_temp[0] =& $form_state;
      array_unshift($args_temp, $form_id);
      $form = call_user_func_array('drupal_retrieve_form', $args_temp);
      $form_build_id = 'form-' . drupal_random_key();
      $form['#build_id'] = $form_build_id;
      drupal_prepare_form($form_id, $form, $form_state);

      // Store a copy of the unprocessed form for caching and indicate that it
      // is cacheable if #cache will be set.
      $original_form = $form;
      $cacheable = TRUE;
    $form['#post'] = $_POST;

    // Now that we know we have a form, we'll process it (validating,
    // submitting, and handling the results returned by its submission
    // handlers. Submit handlers accumulate data in the form_state by
    // altering the $form_state variable, which is passed into them by
    // reference.
    drupal_process_form($form_id, $form, $form_state);
    if ($cacheable && !empty($form['#cache'])) {

      // Caching is done past drupal_process_form so #process callbacks can
      // set #cache.
      form_set_cache($form_build_id, $original_form, $form_state);

  // Most simple, single-step forms will be finished by this point --
  // drupal_process_form() usually redirects to another page (or to
  // a 'fresh' copy of the form) once processing is complete. If one
  // of the form's handlers has set $form_state['redirect'] to FALSE,
  // the form will simply be re-rendered with the values still in its
  // fields.
  // If $form_state['storage'] or $form_state['rebuild'] has been set
  // and input has been processed, we know that we're in a complex
  // multi-part process of some sort and the form's workflow is NOT
  // complete. We need to construct a fresh copy of the form, passing
  // in the latest $form_state in addition to any other variables passed
  // into drupal_get_form().
  if ((!empty($form_state['storage']) || !empty($form_state['rebuild'])) && !empty($form_state['process_input']) && !form_get_errors()) {
    $form = drupal_rebuild_form($form_id, $form_state, $args);

  // If we haven't redirected to a new location by now, we want to
  // render whatever form array is currently in hand.
  return drupal_render_form($form_id, $form);


eikes’s picture

To get a node edit form, you need to include node.pages.inc.

  // required for Drupal 6
  module_load_include('inc', 'node', 'node.pages');  
  // which nodeform you want
  $node_type = 'YOURNODETYPE';
  $form_id = $node_type . '_node_form';
  // maybe add current users info
  global $user;
  // create a blank node
  $node = array(
    'uid' => $user->uid, 
    'name' => (isset($user->name) ? $user->name : ''), 
    'type' => $node_type, 
  // Invoke hook_nodapi and hook_node
  // Or you can also use an exiting node, for example 
  // $node = node_load(123);
  // and the display the form:
  $output = drupal_get_form($form_id, $node);
oadaeh’s picture

However, the node_object_prepare() function takes an object (as the name implies), and your $node is an array.

Something like

  $node->uid = $user->uid;
  $node->name = (isset($user->name) ? $user->name : '');
  $node->type = $node_type;

instead of

  $node = array(
    'uid' => $user->uid,
    'name' => (isset($user->name) ? $user->name : ''),
    'type' => $node_type,

would be better.

supertwang’s picture

if the form you're asking for requires them... (I'm using Drupal 6.15)

took me a while to figure this out... maybe it'll help someone else.

HEre's an example with the watcher module that I'm using successfully...

    module_load_include('inc', 'watcher', 'watcher.db');
    global $user;
    print drupal_get_form('_watcher_user_settings', $user );

where I'm passing the $user to the function. Looking at the function (from watcher.module)

function _watcher_user_settings(&$form_state, $account) {
// ... super usefull code here - thx author!!!

we can see that it is expecting an argument ' $account ' ( a user object ). If I fail to pass the argument as listed in the first code segment, I get errors.

Hope this helps someone...


mattcasey’s picture

To get rid of the errors, I think you just have to declare the $account variable in the function. If you include it, your array will override the empty one.

function _watcher_user_settings(&$form_state, $account = array()) {
// ... super usefull code here - thx author!!!
Scott McCabe’s picture

You can call this function without extra arguments like so:


function module_form() {
  // whatever

Notice that module_form() does not have any arguments.

If you need an extra argument like so:

drupal_get_form('module_form', $extra_arg);

function module_form(&$form_state, $extra_arg) {
  // whatever

Notice that module_form() must add &$form_state and then $extra_arg.

lazos’s picture

I lost an afternoon (and the noon) trying to figure out why my form doesn't contain $node object (when created with drupal_get_form), .
So i think this is reasonable mistake (having in mind drupal 5.x functionality) and this comment has to become a link where ever the drupal_get_form is applied on documentation. That will save afternoons from a lot of people, investigating possible (syntax) errors . I'm sure!

andremaha’s picture

All of you who are learning drupal by the Pro Drupal Development from Apress beware of the mistake in the source code from the second chapter.
When you call your annotate_entry_form() function remember to use the method Scott McCabe described above:

function annotate_entry_form(&$form_state, $node) {
  // Your code goes here and you will have an access to those
  // $node->annotation and $node->nid values at last!

I have spent hours fighting it!!! Thank you very much!

fmitchell’s picture

For D7, the argument is now in a new array.

function module_form(&$form_state, $extra_arg) {
  // whatever

Instead of calling $extra_arg directly (like in D6), it's now $extra_arg['build_info']['args'][0]

ldweeks’s picture

I'm working on submitting a patch to an existing module, and this module has a theme function that was being called, but I couldn't figure out where in the world it was being called from. I found my answer in this function.

You'll notice in the code above that drupal_get_form returns return drupal_render_form($form_id, $form);. You can track down drupal_render_form in form.inc. That function adds the #theme callback to the form array if the #theme callback is registered but not already set in the current form array. (It might be already set if the theme function was being overridden by some *other* module). In this case, however, the module I want to patch *had* registered the theme function, but had not added it to the form array yet. The #theme callback is added in this step, and so the theme function from my module gets called from drupal_render. Mystery solved.

hjc1710’s picture

Not sure why this isn't in the documentation yet. Anyway, due to the nature of how this function grabs arguments (func_get_args()), it is currently not possible to pass an argument by reference through this function. This means this code won't work:

  function someForm_form(&$form_state, &$my_list) {
    //... various code here

Anytime this function is called (regardless of whether an array, variable or reference [using the & operator to get the address of a variable] is used), one will get this error:

warning: Parameter 2 to someForm_form() expected to be a reference, value given in /...../drupal/includes/form.inc on line 377..

There is currently no way around this little annoyance. Additionally, passing an array by value can destroy its structure (say you have numeric keys that are strings, a pass by value resets the keys and renumbers them starting at 0, destroying all keys and their relevance). Thus, a pass by reference is required to get around this issue. To do so, one has to use the $GLOBALS array. That can be done like:

function form_caller() {
  global $theList;
  $theList = array();

  //code to populate $theList
  $output .= drupal_get_form('someForm_form');

function someForm_form(&$form_state){
  $theList =& $GLOBALS['theList'];

Now you can use the array as you intended, with its structure. And the source variable is changeable too (as it should be in a pass by reference scenario).