Can someone show me a proper example of how to create a new content type in a module's hook_install method?

I'd also like to know if/what I should do during hook_uninstall to clean up my mess; I read there is some debate about simply removing the content.

I haven't been able to find an example of the proper method of accomplishing this task, any help would be appreciated!

6 Answers 6


To answer your questions exactly:

Creating a content type in hook install: You use node_type_save() to create the content type, here's an example from webform.install:

  // Create the default webform type.
  $webform_type = array(
    'type' => 'webform',
    'name' => st('Webform'),
    'base' => 'node_content',
    'description' => st('Create a new form or questionnaire accessible to users. Submission results and statistics are recorded and accessible to privileged users.'),
    'custom' => TRUE,
    'modified' => TRUE,
    'locked' => FALSE,

  $webform_type = node_type_set_defaults($webform_type);

What do do in hook_uninstall: Basically just cleanup after yourself, so delete variables your module created (using variable_del()), delete any files uploaded by the module (using file_unmanaged_delete_recursive()), delete defined content types (using node_type_delete()), etc.

Hope this helps!

  • Thank you, it's good to know the coding way to do it as well, and I needed to find some good info on using uninstall
    – Jane Panda
    Mar 15, 2011 at 12:48
  • no problem, glad to help! :)
    – Alex Weber
    Mar 16, 2011 at 17:21

One of the best ways to keep this information in code is to use features. Feaures can put into code:

  • Content types
  • CCK fields
  • Permissions
  • Roles

The list goes on.

One nice feature about features is the drush integration

drush features will give you a list of all features on the site, and their status
drush features revert all will revert all features back to what is in code (really handy for running after deployments)

Help this helps

  • Agreed, although it doesn't answer the question directly...
    – Alex Weber
    Mar 10, 2011 at 22:15
  • Yeah, I do know this is not exact the exact solution requested, but ultimately they will both achieve the same goal, it is just that one will be a nightmare to maintain, and the other will be easy with point and click and awesome drush integration. Bob can decide what works for him
    – wiifm
    Mar 10, 2011 at 22:27
  • 1
    Agreed again! Drush + Features can be intimidating at first but its so useful once you get the hang of it :)
    – Alex Weber
    Mar 11, 2011 at 4:29
  • I'll give that a shot, I think it will save time if I can export a custom content type so I wont have to hand jam all the fields in. Thanks!
    – Jane Panda
    Mar 15, 2011 at 12:48

Check out the D7 Examples module. node_example has install & uninstall.

You may be able to flesh out the type with the interface tools and then make a feature (with features module) & extract relevant parts into the install function of your custom module.


If you want to create a new content type with some fields, you can use the below code.

This code works for me perfectly.

function HOOK_install() {

 $t = get_t();
 $node_example = array(
    'type' => 'slider',
    'name' => $t('Slider Content'),
    'base' => 'node_content',
    'description' => $t('Add slider content.'),
    'body_label' => $t('Slider Description')
 $content_type = node_type_set_defaults($node_example);

// Create a custom Field with our required field-type.
$field = array(
  'field_slider_images' => array (
    'field_name' => 'field_slider_images',
    'type' => 'image',
 'field_slider_links' => array (
   'field_name' => 'field_slider_links',
   'type' => 'text',
   'entity_types' => array('node'),

foreach ($field as $fields) {

// Create a instances of that Field.
$instance = array(
'field_slider_images' => array (
  'field_name' => 'field_slider_images',
  'entity_type' => 'node',
  'bundle' => 'slider',
  'label' => t('Slider Image'),
  'description' => 'Add Slider Image.',
  'settings' => array(
    'file_directory' => 'field/document',
    'file_extensions' => 'png PNG jpg jpeg JPG JPEG',
    'max_filesize' => '10MB',
    'title_field' => '',
  'widget' => array(
     'type' => 'image_image',
     'weight'=> 10,
  'formatter' => array(
    'label' => t('label'),
    'format' => 'image'
  'settings' => array(
    'file_directory' => 'slider-image', // save inside "public://photos"
    'max_filesize' => '4M',
    'preview_image_style' => 'thumbnail',
    'title_field' => TRUE,
    'alt_field' => FALSE,
'field_slider_links' => array (
  'field_name' => 'field_slider_links',
  'entity_type' => 'node',
  'bundle' => 'slider',
  'label' => t('Slider Link'),
  'widget' => array('type' => 'text_textfield'),

foreach ($instance as $fieldinstance) {

$status = node_type_save($content_type);

// Replacement rule for the messages.
$t_args = array('%name' => $content_type->name);
if ($status == SAVED_UPDATED) { // update case
 drupal_set_message($t('The content type %name has been updated.', $t_args));
 elseif ($status == SAVED_NEW) { // create case
   drupal_set_message($t('The content type %name has been added.', $t_args));
   watchdog('node', 'Added content type %name.', $t_args, WATCHDOG_NOTICE, l($t('view'), 'admin/structure/types')); 


I did this in the MySpace Sync module.
I remember piecing that together from various sources, but I don't remember what all the sources were. You can browse the git repository there to see what I did, but it's basically an export of each content type with CCK's Content Copy module on install, and then deleting all nodes before deleting the content type on uninstall.

  • 4
    While the question is tagged drupal-7, MySpace Sync module is only available fro Drupal 6. The API used to create content type has changed a lot between Drupal 6 and Drupal 7. Mar 11, 2011 at 10:11

Try the link below, it will be helpful.

Drupal 7 Field API - Setup a new content type on install and add fields

  • I suggest you expand your answer to add more details about what that link is about (a summary or so of it). It may help to get your downvote (which is not mine ...) removed ... Aug 17, 2016 at 12:06

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.