4.6.x node.php hook_form(&$node, &$param)
4.7.x node.php hook_form(&$node)
5.x node.php hook_form(&$node, $form_values)
6.x node.php hook_form(&$node, $form_state)
7.x node.api.php hook_form($node, &$form_state)

Display a node editing form.

This is a node-type-specific hook, which is invoked only for the node type being affected. See Node API hooks for more information.

Use hook_form_BASE_FORM_ID_alter(), with base form ID 'node_form', to alter node forms for all node types.

This hook, implemented by node modules, is called to retrieve the form that is displayed to create or edit a node. This form is displayed at path node/add/[node type] or node/[node ID]/edit.

The submit and preview buttons, administrative and display controls, and sections added by other modules (such as path settings, menu settings, comment settings, and fields managed by the Field UI module) are displayed automatically by the node module. This hook just needs to return the node title and form editing fields specific to the node type.


$node: The node being added or edited.

$form_state: The form state array.

Return value

An array containing the title and any custom form elements to be displayed in the node editing form.

Related topics

190 functions implement hook_form()

Note: this list is generated by pattern matching, so it may include some functions that are not actually implementations of this hook.

aggregator_admin_form in modules/aggregator/aggregator.admin.inc
Form constructor for the aggregator system settings.
aggregator_page_category_form in modules/aggregator/aggregator.pages.inc
Page callback: Displays a form containing items aggregated in a category.
aggregator_page_source_form in modules/aggregator/aggregator.pages.inc
Page callback: Displays a form with all items captured from a feed.
ajax_forms_test_ajax_commands_form in modules/simpletest/tests/ajax_forms_test.module
Form to display the Ajax Commands.
ajax_forms_test_lazy_load_form in modules/simpletest/tests/ajax_forms_test.module
Form builder: Builds a form that triggers a simple AJAX callback.

... See full list

1 invocation of hook_form()
field_attach_form in modules/field/field.attach.inc
Add form elements for all fields for an entity to a form structure.


modules/node/node.api.php, line 1123
Hooks provided by the Node module.


function hook_form($node, &$form_state) {
  $type = node_type_get_type($node);
  $form['title'] = array(
    '#type' => 'textfield',
    '#title' => check_plain($type->title_label),
    '#default_value' => !empty($node->title) ? $node->title : '',
    '#required' => TRUE,
    '#weight' => -5,
  $form['field1'] = array(
    '#type' => 'textfield',
    '#title' => t('Custom field'),
    '#default_value' => $node->field1,
    '#maxlength' => 127,
  $form['selectbox'] = array(
    '#type' => 'select',
    '#title' => t('Select box'),
    '#default_value' => $node->selectbox,
    '#options' => array(
      1 => 'Option A',
      2 => 'Option B',
      3 => 'Option C',
    '#description' => t('Choose an option.'),
  return $form;


scallopedllama’s picture

The page above implies that you need to add form elements to see the title and the body but that's not quite right. You need to add the form elements to get a title field but body is inserted automatically by the Field API.

What it neglects to tell you is that if you want a body with your custom node type, you need to add a body field to it in your modules install file with an implementation of hook_install like the following:

function mynodetype_install() {
  // Ensure the node type is available.
  $types = node_type_get_types();
  node_add_body_field($types['mynodetype'], 'Body input label');

With that in your install, the body field will be put in your node's edit / add form automatically.

alecspopa’s picture

For Drupal 6 the function equivalent to
node_get_types('type', $node)

cliffp’s picture

I don't think that node_example.module uses this in Drupal 7. I think that this is because all form elements are supplied by the attached fields, so this hook is not needed.

francort’s picture

I'm quite sure you're right.
Remember to file an issue instead of commenting here if you think you've found something wrong with the documentation.(I've done it for this case)


babipanghang’s picture

It has taken me a very long time to figure out how to get this to work, but thanks to jhodgdon, I managed to figure it out. It seemed appropriate for me to post a small example module here, FYI:


name = Contenttype test
description = Test - how can I create a new content type with hook form?
core = 7.x
files[] = contenttypetest.module;
files[] = contenttypetest.install;


    // hook_form implementation
    function contenttypetest_form($node, &$form_state) {
        $form = array();

        $form['contenttypetest_pass'] = array(
            '#type' => 'password',
            '#title' => t('Type a password'),
            '#description' => t('You can type anything you like.'),
        $form['contenttypetest_veld'] = array(
            '#type' => 'file',
            '#description' => 'You might wanna upload a file!',
            '#title' => 'Bestand',
        return $form;

    // hook_node_info() implementation
    function contenttypetest_node_info() {
        return array(
            'contenttypetest' => array(
                'name' => t('Content type test node'),
                'base' => 'contenttypetest',
                'module' => 'contenttypetest',
                'description' => t("This nodetype is a test how to create nodetypes."),
                'help' => 'So this is how your new contenttype looks!',
                'title_label' => t('Test'),
                'has_body' => FALSE,

    // hook_validate() implementation
    function contenttypetest_validate(){


    // hook_uninstall() implementation
    function contenttypetest_uninstall(){
zorroposada’s picture

Thanks for this example.

I found that you should pass the content type name exactly as it appears in your hook_node_info to the node_type_delte function.


    // hook_uninstall() implementation
    function contenttypetest_uninstall(){

This should work fine

sameer.saleh’s picture

i want form for my node ? i create form in drupal 6 not problem but in drupal 7 i can't how to?

this code form in drupal 6

function adv_form(){
  $form_state = array();
  $nodeType = 'adv';
 // create a string of the $form_id
  $form_id = $nodeType.'_node_form';
 // create a basic $node array
   if ( (is_numeric( arg(1) )) && (arg(2) == 'edit')) {
	$node = node_load(arg(1));
   	$node = array('type' => $nodeType, 'uid' => $GLOBALS['user']->uid, 'name' => $GLOBALS['user']->name);
  // load the $form
   $form = drupal_retrieve_form($form_id, $form_state, $node);
   // prepare the $form
   drupal_prepare_form($form_id, $form, $form_state);
  //modify submit button for redirection after save node 
	$form['buttons']['submit'] = array(
		'#type' => 'submit',
		'#value' => t('Save'),
		'#weight' => 5,
		'#submit' => array('public_form_submit'),
   $form['#theme'] = 'adv_form';
   $form['#redirect'] = 'control_panel';
  return $form;
alanom’s picture

... where you'll find complete lists of available #types of element etc etc, along with all the appropriate attributes for each: forms_api_reference.html

shubhkaran’s picture

It's interesting to note that you need to implement this function for the custom 'content/node type' to show up for editing under 'admin/structure/types'. Discovered this while implementing a custom node type step by step.

bcorne’s picture

I've been trying to figure out how to override the behaviour from clicking save or preview on a form rendered via the hook_form method.

So far I have this:

function mc_form() {
    $form['#submit'] = array('_mc_form_submit');

function _mc_form_submit($form,&$form_state) {
    $node = ...;
    /* do some stuff with node */


function mc_install() {
    // disable the preview button for mc
    variable_set('node_preview_mc', '0');

But I have the following problem: Some other submit handler is triggered, still no idea which one, no idea how to stop this. This causes the node to be saved twice.

For preview the #submit handler also gets triggered, that's why I disabled the preview possibility in hook_install().

So the real question: Am I doing this right? If so, how do I prevent the second submission. If not, is there a better approach (I need form access, so hook_node_save() and the likes aren't applicable)

bcorne’s picture

As a possible solution to prevent second submission,
I used drupal_goto() statement in _mc_form_submit(), after saving it.
This in combination with no possibility to preview seems to be ok.

function mc_form_submit($form,&$form_state) {
   $node = new stdClass;
   $node->type = 'mc';
   //more assignments