How to Create Custom Post Type in WordPress

WordPress can be configured to hold and display many different types of content. That makes it very flexible and capable of customizing according to our needs. By default, we get several types of content in WordPress, called post types.

The ones we use the most are post type post and post type page (Link to full list of post types in WordPress). But what if we want to add other post types like review, portfolio, recipe, or whatever you want? We use custom post types.

The custom post type is additional type of post that you can create in your WordPress. This can be done with the help of WP’s register_post_type function. These post types can be customized from almost every angle, giving our WordPress unique look and feel.

So, how can you add a custom post type?

Two ways – using a third-party plugin or applying your custom code snippet to your theme’s functions.php file.

Using a plugin

If you don’t like editing the WordPress  files, then you maybe prefer using a plugin to achieve certain functionality. The most popular one for creating and editing custom post types is Custom Post Type UI. It’s very intuitive, and you shouldn’t have problems setting your custom post type.

I won’t get into details of how to use this plugin, since the core of this post is to show you how to do it manually. The drawback of using a plugin is that when it’s not active, your post types are not displaying.

Code snippet for custom post type

For the purpose of this demo, let’s create a new post type for books for our new book website. Remember, the code we are going to write must be put in our theme’s functions.php file, which is usually located in wp-content/themes/theme-name/functions.php.

So basically, the boilerplate code for the custom types is the following:

function books_post() {
 
 $labels = array(
 'name' => 'Books',
 'singular_name' => 'Book', 
 'menu_name' => 'Books Review', 
 'name_admin_bar' => 'Book', 
 'add_new_item' => 'Add New Book', 
 'new_item' => 'New Book', 
 'edit_item' => 'Edit Book',
 'view_item' => 'View Book', 
 'all_items' => 'All Books', 
 'featured_image' => 'Book Cover',
 'set_featured_image' => 'Set book cover', 
 'remove_featured_image' => 'Remove book cover'
 );

 $args = array(
 'label' => 'Book',
 'labels' => $labels,
 'supports' => array( 'title', 'editor', 'author', 'thumbnail', 'comments', 'custom-fields', ),
 'taxonomies' => array( 'category', 'post_tag' ),
 'hierarchical' => false,
 'public' => true,
 'show_ui' => true,
 'show_in_menu' => true,
 'menu_position' => 20,
 'show_in_admin_bar' => true,
 'show_in_nav_menus' => true,
 'has_archive' => true, 
 'exclude_from_search' => false,
 'publicly_queryable' => true,
 );

 register_post_type( 'books', $args );

}

add_action( 'init', 'books_post' );

This piece of code will create a new custom post type for books, containing few other options which I am going to explain now.

  • books_post() – the name of our custom function. Can be named anything you want.

Then we have the array $labels which are custom labels for our custom post type. Let’s see what they mean:

  • name – General name for the post type, usually plural.
  • singular_name – Name for single object of this post type. Default is Post/Page.
  • menu_name – Name used in the menu.
  • name_admin_bar – String for use in New in Admin menu bar.
  • add_new_item – Default is Add New Post/Add New Page.
  • new_item – Default is New Post/New Page.
  • edit_item – Default is Edit Post/Edit Page.
  • view_item – Default is View Post/View Page.
  • all_items – String for the sub-menu.
  • featured_image – Default is Featured image.
  • set_featured_image – Default is Set Featured Image.
  • remove_featured_image – Default is Remove featured image.

Then we have the $args array, which is our main configuration for the custom post type. Let’s break this into pieces as well, and see what each thing does.

  • label – A plural descriptive name for the post type marked for translation. If you don’t declare a custom label, WordPress will use the name of the custom post type by default
  • labels – Applying our labels array from above
  • supports – This is usually an array of features that the custom post type will support.
  • taxonomies – An array of registered taxonomies like category or post_tag that will be used with this post type.
  • hierarchical – Whether the post type is hierarchical. Or whether or not you can declare a parent page, child page, etc… of the post type. This is mainly intended for Pages. Here we declare it false so there’s no need to worry about it for our example.
  • public – Whether a post type is intended to be used publicly either via the admin interface or by front-end users.
  • show_ui – Generates a default UI for managing this post type in the admin.
  • show_in_menu – Whether the custom post type should be visible in the menu.
  • menu_position – The position in the menu order the post type should appear;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.
  • show_in_admin_bar – Whether to make this post type available in the WordPress admin bar.
  • show_in_nav_menus – Whether post_type is available for selection in navigation menus
  • has_archive – Enables post type archives.
  • exclude_from_search – Exclude from the search engine.
  • publicly_queryable – Whether queries can be performed on the front end.

And after that we register our post type with the register_post_type function, which takes two parameters – our post type and our $args array. If you copy this code and paste it in your functions.php, a new post type will appear in your dashboard called Books.

From there you can Add New Book, Edit Book, Set Book Cover, etc. See, it’s not so complicated when you know what each line does.

Now you can change this code and create almost anything you want. Even more, I will give you an extended code snippet below with more options and all comments. Use it as a sample for your next project.

// Register Custom Post Type

function books_post() {
 // Array of custom labels for our custom post type backend.
 $labels = array(
 'name' => 'Books', //general name for the post type, usually plural. Default is Posts/Pages
 'singular_name' => 'Book', //name for single object of this post type. Default is Post/Page
 'menu_name' => 'Books', // Name used in the menu
 'name_admin_bar' => 'Book', // String for use in New in Admin menu bar. - New Book
 'add_new_item' => 'Add New Book', // Default is Add New Post/Add New Page.
 'new_item' => 'New Book', // Default is New Post/New Page.
 'edit_item' => 'Edit Book', // Default is Edit Post/Edit Page.
 'view_item' => 'View Book', // Default is View Post/View Page.
 'all_items' => 'All Books', // String for the submenu. Default is All Posts/All Pages.
 'search_items' => 'Search Books', // Default is Search Posts/Pages
 'parent_item_colon' => 'Parent Books:', // This string is used in hierarchical types. The default is 'Parent Page:'.
 'not_found' => 'No books found.', // Default is No posts found/No pages found.
 'not_found_in_trash' => 'No books found in Trash.', //Default is No posts found in Trash/No pages found in Trash.
 'featured_image' => 'Book Cover', // Default is Featured Image.
 'set_featured_image' => 'Set book cover', // Default is Set featured image.
 'remove_featured_image' => 'Remove book cover', // Default is Remove featured image.
 'use_featured_image' => 'Use as book cover', // Default is Use as featured image.
 'insert_into_item' => 'Insert into item', // String for the media frame button. Default is Insert into post/Insert into page.
 'uploaded_to_this_item' => 'Uploaded to this item', // - String for the media frame filter. Default is Uploaded to this post/Uploaded to this page.
 'items_list' => 'Books list', // String for the table hidden heading.
 'items_list_navigation' => 'Books list navigation', // String for the table pagination hidden heading.
 'filter_items_list' => 'Filter items list' // String for the table views hidden heading.
 );

 $args = array(
 // A plural descriptive name for the post type marked for translation. If you don’t declare a custom label, WordPress will use the name of the custom post type by default.
 'label' => 'Book',
 // Applying our labels array from above.
 'labels' => $labels,
 // This is usually an array of features that the custom post type will support. Here we have quite a long list. These will tie into the admin area.
 'supports' => array( 'title', 'editor', 'author', 'thumbnail', 'comments', 'custom-fields', ),
 // An array of registered taxonomies like category or post_tag that will be used with this post type. Custom taxonomies still need to be registered with register_taxonomy().
 'taxonomies' => array( 'category', 'post_tag' ),
 // Whether the post type is hierarchical (e.g. page). Or whether or not you can declare a parent page, child page, etc… of the post type. This is mainly intended for Pages. Here we declare it false so there’s no need to worry about it for our example.
 'hierarchical' => false,
 // Whether a post type is intended to be used publicly either via the admin interface or by front-end users. WordPress sets this to false by default.
 'public' => true,
 // Generates a default UI for managing this post type in the admin.
 'show_ui' => true,
 // Whether the custom post type should be visible in the menu.
 'show_in_menu' => true,
 // The position in the menu order the post type should appear;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.
 'menu_position' => 20,
 // This declares a custom icon for the admin area. For more dashicons see the original WordPress documentation.
 'menu_icon' => 'dashicons-format-aside',
 // Whether to make this post type available in the WordPress admin bar.
 'show_in_admin_bar' => true,
 //Whether post_type is available for selection in navigation menus.
 'show_in_nav_menus' => true,
 // Enables post type archives.
 'has_archive' => true,
 // Exclude for search engine 
 'exclude_from_search' => false,
 // Whether queries can be performed on the front end as part of parse_request()
 'publicly_queryable' => true,
 // Here we can declare what type of custom post type we will be dealing with. It is used to build the read, edit, and delete capabilities of a post or page. You can choose either post or page.
 'capability_type' => 'page',
 // Whether to expose this post type in the REST API.
 'show_in_rest' => true
 );
 // The register_post_type() is a function that WordPress recognizes as a custom post type generator. In this example it accepts two parameters which are the name of the post type itself and any arguments you would like to call.
 register_post_type( 'books', $args );
}

// This line of code returns or calls our function so it fires and displays within our site.
add_action( 'init', 'books_post' );

You can also take it directly from my GitHub here.

Note

Since we are putting this into our theme’s functions.php file, every time we update or switch the theme, we are going to lose that piece of code. This may put us in very uncomfortable situation, especially if we are building the website for a client.

If you want to avoid this, my tip is to use a child theme, where you can modify the functions.php file however you want and not be worried about future updates.