Ready to get started?Download WordPress


Attention Interested in functions, hooks, classes, or methods? Check out the new WordPress Code Reference!

Difference between revisions of "Function Reference/register post type"

m (Return Values: linking to WP_Error)
(Reserved Post Types)
Line 23: Line 23:
In addition, the following post types should not be used as they interfere with other WordPress functions.
In addition, the following post types should not be used as they interfere with other WordPress functions.
* action
* action
* order
More information: [[Post Types]].
More information: [[Post Types]].

Revision as of 21:24, 20 August 2013


Create or modify a post type. register_post_type should only be invoked through the 'init' action. It won't work at all if called before 'init', and aspects of the new post type will work incorrectly if called later.


It's important to note, that you should in every way, always register your taxonomies using the taxonomies argument when you register your post type. Else they won't get recognized as connected when using filters like parse_query or pre_get_posts, which can lead to unexpected results and failure.

Reserved Post Types

The following post types are reserved and used by WordPress already.

  • post
  • page
  • attachment
  • revision
  • nav_menu_item

In addition, the following post types should not be used as they interfere with other WordPress functions.

  • action
  • order

More information: Post Types.


<?php register_post_type$post_type$args ?>


(string) (required) Post type. (max. 20 characters, can not contain capital letters or spaces)
Default: None
(array) (optional) An array of arguments.
Default: None


(string) (optional) A plural descriptive name for the post type marked for translation.
Default: $post_type
(array) (optional) labels - An array of labels for this post type. By default post labels are used for non-hierarchical types and page labels for hierarchical ones.
Default: if empty, name is set to label value, and singular_name is set to name value

  • 'name' - general name for the post type, usually plural. The same as, and overridden by $post_type_object->label
  • 'singular_name' - name for one object of this post type. Defaults to value of name
  • 'menu_name' - the menu name text. This string is the name to give menu items. Defaults to value of name
  • 'all_items' - the all items text used in the menu. Default is the Name label
  • 'add_new' - the add new text. The default is Add New for both hierarchical and non-hierarchical types. When internationalizing this string, please use a gettext context matching your post type. Example: _x('Add New', 'product');
  • 'add_new_item' - the add new item text. Default is Add New Post/Add New Page
  • 'edit_item' - the edit item text. Default is Edit Post/Edit Page
  • 'new_item' - the new item text. Default is New Post/New Page
  • 'view_item' - the view item text. Default is View Post/View Page
  • 'search_items' - the search items text. Default is Search Posts/Search Pages
  • 'not_found' - the not found text. Default is No posts found/No pages found
  • 'not_found_in_trash' - the not found in trash text. Default is No posts found in Trash/No pages found in Trash
  • 'parent_item_colon' - the parent text. This string isn't used on non-hierarchical types. In hierarchical ones the default is Parent Page
(string) (optional) A short descriptive summary of what the post type is.
Default: blank
(boolean) (optional) Whether a post type is intended to be used publicly either via the admin interface or by front-end users.
Default: false
  • 'false' - Post type is not intended to be used publicly and should generally be unavailable in wp-admin and on the front end unless explicitly planned for elsewhere.
  • 'true' - Post type is intended for public use. This includes on the front end and in wp-admin.
Note: While the default settings of exclude_from_search, publicly_queryable, show_ui, and show_in_nav_menus are inherited from public, each does not rely on this relationship and controls a very specific intention.
(boolean) (importance) Whether to exclude posts with this post type from front end search results.
Default: value of the opposite of the public argument
  • 'true' - site/?s=search-term will not include posts of this post type.
  • 'false' - site/?s=search-term will include posts of this post type.
Note: If you want to show the posts's list that are associated to taxonomy's terms, you must set exclude_from_search to false (ie : for call site_domaine/?taxonomy_slug=term_slug or site_domaine/taxonomy_slug/term_slug). If you set to true, on the taxonomy page (ex: taxonomy.php) worpress will not find your posts and/or pagination will make 404 error...
(boolean) (optional) Whether queries can be performed on the front end as part of parse_request().
Default: value of public argument
Note: The queries affected include the following (also initiated when rewrites are handled)
  •  ?post_type={post_type_key}
  •  ?{post_type_key}={single_post_slug}
  •  ?{post_type_query_var}={single_post_slug}
Note: If you set this to FALSE, you will find that you cannot preview/see your custom post (return 404).
(boolean) (optional) Whether to generate a default UI for managing this post type in the admin.
Default: value of public argument
  • 'false' - do not display a user-interface for this post type
  • 'true' - display a user-interface (admin panel) for this post type
Note: _built-in post types, such as post and page, are intentionally set to false.
(boolean) (optional) Whether post_type is available for selection in navigation menus.
Default: value of public argument
(boolean or string) (optional) Where to show the post type in the admin menu. show_ui must be true.
Default: value of show_ui argument
  • 'false' - do not display in the admin menu
  • 'true' - display as a top level menu
  • 'some string' - If an existing top level page such as 'tools.php' or 'edit.php?post_type=page', the post type will be placed as a sub menu of that.
Note: When using 'some string' to show as a submenu of a menu page created by a plugin, this item will become the first submenu item, and replace the location of the top level link. If this isn't desired, the plugin that creates the menu page needs to set the add_action priority for admin_menu to 9 or lower.
Note: As this one inherits its value from show_ui, which inherits its value from public, it seems to be the most reliable property to determine, if a post type is meant to be publicly useable. At least this works for _builtin post types and only gives back post and page.
(boolean) (optional) Whether to make this post type available in the WordPress admin bar.
Default: value of the show_in_menu argument
(integer) (optional) The position in the menu order the post type should appear. show_in_menu must be true.
Default: null - defaults to below Comments
  • 5 - below Posts
  • 10 - below Media
  • 15 - below Links
  • 20 - below Pages
  • 25 - below comments
  • 60 - below first separator
  • 65 - below Plugins
  • 70 - below Users
  • 75 - below Tools
  • 80 - below Settings
  • 100 - below second separator
(string) (optional) The url to the icon to be used for this menu.
Default: null - defaults to the posts icon
(string or array) (optional) The string to use to build the read, edit, and delete capabilities. May be passed as an array to allow for alternative plurals when using this argument as a base to construct the capabilities, e.g. array('story', 'stories'). By default the capability_type is used as a base to construct capabilities. It seems that `map_meta_cap` needs to be set to true, to make this work.
Default: "post"
Some of the capability types that can be used (probably not exhaustive list):
  • post (default)
  • page
These built-in types cannot be used:
  • attachment
  • mediapage
(array) (optional) An array of the capabilities for this post type.
Default: capability_type is used to construct
By default, seven keys are accepted as part of the capabilities array:
  • edit_post, read_post, and delete_post - These three are meta capabilities, which are then generally mapped to corresponding primitive capabilities depending on the context, for example the post being edited/read/deleted and the user or role being checked. Thus these capabilities would generally not be granted directly to users or roles.
  • edit_posts - Controls whether objects of this post type can be edited.
  • edit_others_posts - Controls whether objects of this type owned by other users can be edited. If the post type does not support an author, then this will behave like edit_posts.
  • publish_posts - Controls publishing objects of this post type.
  • read_private_posts - Controls whether private objects can be read.
Note: those last four primitive capabilities are checked in core in various locations.
There are also seven other primitive capabilities which are not referenced directly in core, except in map_meta_cap(), which takes the three aforementioned meta capabilities and translates them into one or more primitive capabilities that must then be checked against the user or role, depending on the context. These additional capabilities are only used in map_meta_cap(). Thus, they are only assigned by default if the post type is registered with the 'map_meta_cap' argument set to true (default is false).
  • read - Controls whether objects of this post type can be read.
  • delete_posts - Controls whether objects of this post type can be deleted.
  • delete_private_posts - Controls whether private objects can be deleted.
  • delete_published_posts - Controls whether published objects can be deleted.
  • delete_others_posts - Controls whether objects owned by other users can be can be deleted. If the post type does not support an author, then this will behave like delete_posts.
  • edit_private_posts - Controls whether private objects can be edited.
  • edit_published_posts - Controls whether published objects can be edited.

If you assign a 'capability_type' and then take a look into the $GLOBALS['wp_post_types']['your_cpt_name'] array, then you'll see the following:

[cap] => stdClass Object
	[edit_post]		 => "edit_{$capability_type}"
	[read_post]		 => "read_{$capability_type}"
	[delete_post]		 => "delete_{$capability_type}"
	[edit_posts]		 => "edit_{$capability_type}s"
	[edit_others_posts]	 => "edit_others_{$capability_type}s"
	[publish_posts]		 => "publish_{$capability_type}s"
	[read_private_posts]	 => "read_private_{$capability_type}s"
        [delete_posts]           => "delete_{$capability_type}s"
        [delete_private_posts]   => "delete_private_{$capability_type}s"
        [delete_published_posts] => "delete_published_{$capability_type}s"
        [delete_others_posts]    => "delete_others_{$capability_type}s"
        [edit_private_posts]     => "edit_private_{$capability_type}s"
        [edit_published_posts]   => "edit_published_{$capability_type}s"

Note the "s" at the end of plural capabilities.

(boolean) (optional) Whether to use the internal default meta capability handling.
Default: false
(boolean) (optional) Whether the post type is hierarchical (e.g. page). Allows Parent to be specified. The 'supports' parameter should contain 'page-attributes' to show the parent select box on the editor page.
Default: false
Note: this parameter was planned for Pages. Be careful, when choosing it for your custom post type - if you are planning to have many entries (say - over 100), you will run into memory issue. With this parameter set to true WordPress will fetch all entries of that particular post type, together with all meta data, on each administration page load for your post type.
(array/boolean) (optional) An alias for calling add_post_type_support() directly. As of 3.5, boolean false can be passed as value instead of an array to prevent default (title and editor) behavior.
Default: title and editor
  • 'title'
  • 'editor' (content)
  • 'author'
  • 'thumbnail' (featured image, current theme must also support post-thumbnails)
  • 'excerpt'
  • 'trackbacks'
  • 'custom-fields'
  • 'comments' (also will see comment count balloon on edit screen)
  • 'revisions' (will store revisions)
  • 'page-attributes' (menu order, hierarchical must be true to show Parent option)
  • 'post-formats' add post formats, see Post Formats
Note: When you use custom post type that use thumbnails remember to check that the theme also supports thumbnails or use add_theme_support function.
(string) (optional) Provide a callback function that will be called when setting up the meta boxes for the edit form. Do remove_meta_box() and add_meta_box() calls in the callback.
Default: None
(array) (optional) An array of registered taxonomies like category or post_tag that will be used with this post type. This can be used in lieu of calling register_taxonomy_for_object_type() directly. Custom taxonomies still need to be registered with register_taxonomy().
Default: no taxonomies
(boolean or string) (optional) Enables post type archives. Will use $post_type as archive slug by default.
Default: false
Note: Will generate the proper rewrite rules if rewrite is enabled. Also use rewrite to change the slug used.
(string) (optional) The default rewrite endpoint bitmasks. For more info see Trac Ticket 12605 and this Make WordPress Plugins summary of endpoints.
Note: In 3.4, this argument is effectively replaced by the 'ep_mask' argument under rewrite.
(boolean or array) (optional) Triggers the handling of rewrites for this post type. To prevent rewrites, set to false.
Default: true and use $post_type as slug
$args array
  • 'slug' => string Customize the permastruct slug. Defaults to the $post_type value. Should be translatable.
  • 'with_front' => bool Should the permastruct be prepended with the front base. (example: if your permalink structure is /blog/, then your links will be: false->/news/, true->/blog/news/). Defaults to true
  • 'feeds' => bool Should a feed permastruct be built for this post type. Defaults to has_archive value.
  • 'pages' => bool Should the permastruct provide for pagination. Defaults to true
  • 'ep_mask' => const As of 3.4 Assign an endpoint mask for this post type. For more info see Trac Ticket 19275 and this Make WordPress Plugins summary of endpoints.
    • If not specified and permalink_epmask is set, inherits from permalink_epmask
    • If not specified and permalink_epmask is not set, defaults to EP_PERMALINK
Note: If registering a post type inside of a plugin, call flush_rewrite_rules() in your activation and deactivation hook (see Flushing Rewrite on Activation below). If flush_rewrite_rules() is not used, then you will have to manually go to Settings > Permalinks and refresh your permalink structure before your custom post type will show the correct structure.
(boolean or string) (optional) Sets the query_var key for this post type.
Default: true - set to $post_type
  • 'false' - Disables query_var key use. A post type cannot be loaded at /?{query_var}={single_post_slug}
  • 'string' - /?{query_var_string}={single_post_slug} will work as intended.
Note: The query_var parameter has no effect if the ‘publicly_queryable’ parameter is set to false. query_var adds the custom post type’s query var to the built-in query_vars array so that WordPress will recognize it. WordPress removes any query var not included in that array.
If set to true it allow you to request a custom posts type (book) using this: example.com/?book=life-of-pi
If set to a string rather than true (for example ‘publication’), you can do: example.com/?publication=life-of-pi

(boolean) (optional) Can this post_type be exported.
Default: true
(boolean) (not for general use) Whether this post type is a native or "built-in" post_type. Note: this Codex entry is for documentation - core developers recommend you don't use this when registering your own post type
Default: false
  • 'false' - default this is a custom post type
  • 'true' - this is a built-in native post type (post, page, attachment, revision, nav_menu_item)
(boolean) (not for general use) Link to edit an entry with this post type. Note: this Codex entry is for documentation '-' core developers recommend you don't use this when registering your own post type
  • 'post.php?post=%d'

Return Values

(object | WP_Error
The registered post type object, or an error object.


An example of registering a post type called "book".


function codex_custom_init() {
    $args = array( 'public' => true, 'label' => 'Books' );
    register_post_type( 'book', $args );
add_action( 'init', 'codex_custom_init' );


function codex_custom_init() {
  $labels = array(
    'name' => 'Books',
    'singular_name' => 'Book',
    'add_new' => 'Add New',
    'add_new_item' => 'Add New Book',
    'edit_item' => 'Edit Book',
    'new_item' => 'New Book',
    'all_items' => 'All Books',
    'view_item' => 'View Book',
    'search_items' => 'Search Books',
    'not_found' =>  'No books found',
    'not_found_in_trash' => 'No books found in Trash', 
    'parent_item_colon' => '',
    'menu_name' => 'Books'

  $args = array(
    'labels' => $labels,
    'public' => true,
    'publicly_queryable' => true,
    'show_ui' => true, 
    'show_in_menu' => true, 
    'query_var' => true,
    'rewrite' => array( 'slug' => 'book' ),
    'capability_type' => 'post',
    'has_archive' => true, 
    'hierarchical' => false,
    'menu_position' => null,
    'supports' => array( 'title', 'editor', 'author', 'thumbnail', 'excerpt', 'comments' )

  register_post_type( 'book', $args );
add_action( 'init', 'codex_custom_init' );

Customizing the messages:

//add filter to ensure the text Book, or book, is displayed when user updates a book 

function codex_book_updated_messages( $messages ) {
  global $post, $post_ID;

  $messages['book'] = array(
    0 => '', // Unused. Messages start at index 1.
    1 => sprintf( __('Book updated. <a href="%s">View book</a>', 'your_text_domain'), esc_url( get_permalink($post_ID) ) ),
    2 => __('Custom field updated.', 'your_text_domain'),
    3 => __('Custom field deleted.', 'your_text_domain'),
    4 => __('Book updated.', 'your_text_domain'),
    /* translators: %s: date and time of the revision */
    5 => isset($_GET['revision']) ? sprintf( __('Book restored to revision from %s', 'your_text_domain'), wp_post_revision_title( (int) $_GET['revision'], false ) ) : false,
    6 => sprintf( __('Book published. <a href="%s">View book</a>', 'your_text_domain'), esc_url( get_permalink($post_ID) ) ),
    7 => __('Book saved.', 'your_text_domain'),
    8 => sprintf( __('Book submitted. <a target="_blank" href="%s">Preview book</a>', 'your_text_domain'), esc_url( add_query_arg( 'preview', 'true', get_permalink($post_ID) ) ) ),
    9 => sprintf( __('Book scheduled for: <strong>%1$s</strong>. <a target="_blank" href="%2$s">Preview book</a>', 'your_text_domain'),
      // translators: Publish box date format, see http://php.net/date
      date_i18n( __( 'M j, Y @ G:i' ), strtotime( $post->post_date ) ), esc_url( get_permalink($post_ID) ) ),
    10 => sprintf( __('Book draft updated. <a target="_blank" href="%s">Preview book</a>', 'your_text_domain'), esc_url( add_query_arg( 'preview', 'true', get_permalink($post_ID) ) ) ),

  return $messages;
add_filter( 'post_updated_messages', 'codex_book_updated_messages' );

Adding contextual help:

//display contextual help for Books

function codex_add_help_text( $contextual_help, $screen_id, $screen ) { 
  //$contextual_help .= var_dump( $screen ); // use this to help determine $screen->id
  if ( 'book' == $screen->id ) {
    $contextual_help =
      '<p>' . __('Things to remember when adding or editing a book:', 'your_text_domain') . '</p>' .
      '<ul>' .
      '<li>' . __('Specify the correct genre such as Mystery, or Historic.', 'your_text_domain') . '</li>' .
      '<li>' . __('Specify the correct writer of the book.  Remember that the Author module refers to you, the author of this book review.', 'your_text_domain') . '</li>' .
      '</ul>' .
      '<p>' . __('If you want to schedule the book review to be published in the future:', 'your_text_domain') . '</p>' .
      '<ul>' .
      '<li>' . __('Under the Publish module, click on the Edit link next to Publish.', 'your_text_domain') . '</li>' .
      '<li>' . __('Change the date to the date to actual publish this article, then click on Ok.', 'your_text_domain') . '</li>' .
      '</ul>' .
      '<p><strong>' . __('For more information:', 'your_text_domain') . '</strong></p>' .
      '<p>' . __('<a href="http://codex.wordpress.org/Posts_Edit_SubPanel" target="_blank">Edit Posts Documentation</a>', 'your_text_domain') . '</p>' .
      '<p>' . __('<a href="http://wordpress.org/support/" target="_blank">Support Forums</a>', 'your_text_domain') . '</p>' ;
  } elseif ( 'edit-book' == $screen->id ) {
    $contextual_help = 
      '<p>' . __('This is the help screen displaying the table of books blah blah blah.', 'your_text_domain') . '</p>' ;
  return $contextual_help;
add_action( 'contextual_help', 'codex_add_help_text', 10, 3 );

Adding WordPress 3.3+ Help Tab:

function codex_custom_help_tab() {
	global $post_ID;
	$screen = get_current_screen();

	if( isset($_GET['post_type']) ) $post_type = $_GET['post_type'];
	else $post_type = get_post_type( $post_ID );

	if( $post_type == 'book' ) :

		$screen->add_help_tab( array(
			'id' => 'you_custom_id', //unique id for the tab
			'title' => 'Custom  Help', //unique visible title for the tab
			'content' => '<h3>Help Title</h3><p>Help content</p>',  //actual help text



add_action('admin_head', 'codex_custom_help_tab');

Flushing Rewrite on Activation

To get permalinks to work when you activate the plugin use the following example, paying attention to how my_cpt_init is called in the register_activation_hook callback:

add_action( 'init', 'my_cpt_init' );
function my_cpt_init() {
    register_post_type( ... );

function my_rewrite_flush() {
    // First, we "add" the custom post type via the above written function.
    // Note: "add" is written with quotes, as CPTs don't get added to the DB,
    // They are only referenced in the post_type column with a post entry, 
    // when you add a post of this CPT.

    // ATTENTION: This is *only* done during plugin activation hook in this example!
    // You should *NEVER EVER* do this on every page load!!
register_activation_hook( __FILE__, 'my_rewrite_flush' );

For themes, you'll need to use the after_switch_theme hook instead. Like so:

add_action( 'init', 'my_cpt_init' );
function my_cpt_init() {
    register_post_type( ... );

function my_rewrite_flush() {
add_action( 'after_switch_theme', 'my_rewrite_flush' );


  • Note that although the $public attribute is optional, the inputs passed to the register_post_type() function are exactly what is queried by the get_post_types() function. So if you verbosely set the equivalent options for publicly_queriable, show_ui, show_in_nav_menus, and exclude_from_search, this will not be handled the same as if you had set the $public attribute. See bug 18950.

Change Log

  • 3.5.0: 'supports' argument can be set to Boolean false to disable default title and editor.
  • Since 2.9

Source File

register_post_type() is located in wp-includes/post.php.





Post Types: register_post_type(), add_post_type_support(), remove_post_type_support(), post_type_supports(), post_type_exists(), set_post_type(), get_post_type(), get_post_types(), get_post_type_object(), get_post_type_capabilities(), get_post_type_labels(), is_post_type_hierarchical(), is_post_type_archive(), post_type_archive_title()

See also index of Function Reference and index of Template Tags.