The Definitive Guide to WordPress Custom Post Types

Share this article

Gone are the days when WordPress was referred to as just blogging software.

The ability to extend its functionality via plugins and themes, group posts or structured data into various types and also the arrival of the WP Rest API into core, has seen it further evolve into a full blown content management system and development platform.

Over the years, I have successfully developed a number of custom web applications built on-top WordPress that heavily utilize custom post types. An example of one of these web applications is the theme marketplace of my WordPress plugin called ProfilePress.

On a closer look at the theme marketplace linked above, you will discover that it uses a custom pagination system evident in the URL of the Next / Prev pagination links. Thus, it is possible for you to build an in-house pagination system that will work perfectly in querying a custom post type (CPT).

Enough said, let’s get down to the purpose of this tutorial which is, learning the intricacies of custom post types in WordPress.

I hope to address the many hurdles I have personally encountered working with CPTs as well as some cool stuff you can do with it.

Custom Post Type Definition

WordPress can hold and display many different types of content. A single item of such content is generally called a post, although a post is also a specific post type.

Internally, all the post types are stored in the same place, in the wp_posts database table, but are differentiated by a column called post_type.

Post type refers to the various structured data grouped together that is maintained in the WordPress database posts table.

Examples of post types are post (a group of blog posts), page (a group of pages), attachment (a group of uploaded media files), and revision (a groups of post revisions) that are native or built-in to WordPress.

Armed with the definition of post type, a new post type that is created and registered to WordPress is referred to as a custom post type.

If you’re building a company or business website with WordPress, examples of post types you could create are Portfolio, Testimonials and Products.

Now that we’ve understood the concept of custom post types, up next is learning how to create them.

Creating Custom Post Types

Creating a custom post type is pretty easy. Firstly, register the post type with the register_post_type() function and finally, wrap register_post_type() in a function call and hook it to the init Action like so:

function portfolio_cpt() {

    $args = array(
        'label'  => 'Portfolio',
        'public' => true,

    register_post_type( 'portfolio', $args );

add_action( 'init', 'portfolio_cpt' );

From the code above, you can see register_post_type() has a second function parameter that accepts a number of array arguments necessary in customizing every aspect of a custom post type.

You should now see the Portfolio custom post type appear in WordPress admin dashboard.

Custom Post Type - Portfolio

It’s worth noting that when calling register_post_type(), the second argument is optional. That is, a CPT can be also created as follows:

function portfolio_cpt() {

    register_post_type( 'portfolio' );

add_action( 'init', 'portfolio_cpt' );

If a CPT is created in this manner, it won’t show up in the admin dashboard menu (albeit still accessible via direct URL access ““) and its UI wordings (otherwise referred to as labels) and admin notices will be the same as the built-in post post type.

Let’s go over some of the array arguments for customising CPTs and their respective functions.


A plural descriptive name for your custom post type. For example, if you are creating a movie CPT, this should be Movies.

It will default to the value of $post_type which is the first parameter of register_post_type().


An array of labels for this post type. Each string is a bit of text displayed in a particular admin page.

Be sure to make these strings translatable if you’re creating a plugin for public use.

  • name: The plural form of the name of your post type.
  • singular_name: The singular form of the name of your post type.
  • add_new: The menu item for adding a new post.
  • add_new_item: The header shown when creating a new post.
  • edit_item: The header shown when editing a post.
  • new_item: Shown in the favorites menu in the admin header.
  • view_item: Shown alongside the permalink on the edit post screen.
  • search_items: Button text for the search box on the edit posts screen.
  • not_found: Text to display when no posts are found through search in the admin.
  • not_found_in_trash: Text to display when no posts are in the trash.

A full list of labels and their descriptions can be found here.


A short descriptive summary of what the post type is, although I haven’t found where this is used in WordPress admin.


Depending on its Boolean value, it’ll automatically decide what other arguments should be unless they’re specifically defined. If you’re looking to have more control over the public arguments, there are three specific arguments you may set:

  • show_ui: determines whether to show the administration screens.
  • publicly_queryable: determines whether queries for this post type can be performed from the front end.
  • exclude_from_search: whether the posts should appear in search results.

By default, a new post type is added after the ‘Comments’ menu item in the admin. But you have to ability to move it to a suitable position of your choosing.

For example, setting the menu_position value to 70 will add your menu item below Users.

New post types will default to the Posts menu icon, but if you want a custom icon in it instead, set this label to the URL of the icon or image file.

'menu_icon' => get_stylesheet_directory_uri() . '/images/portfolio-icon.png',

You can also use any dashicon as your CPT icon.

Say you want to use the download dashicon, set this label to the dashicon value as follows:

'menu_icon' => 'dashicons-download',


This argument allows you to decide whether to make your CPT hierarchical or not. The default value is false. Set to true to make your CPT hierarchical.


The supports argument allows you to define an array of meta boxes and fields that will appear on the screen when editing or creating a new post. This defaults to title and editor.

Setting this argument to false will prevent the default (title and editor) behavior.

There are several available options:

  • title: Text input field to create a post title.
  • editor: Content TinyMCE editor for writing.
  • author: A select box for changing the post author.
  • thumbnail: Featured image capability.
  • excerpt: A textarea for writing a custom excerpt.
  • trackbacks: Ability to turn trackbacks and pingbacks on/off.
  • custom-fields: Custom fields input field.
  • comments: Turn comments on/off.
  • revisions: Allows revisions to be made of your post.
  • post-formats: Add post formats, see the ‘Post Formats’ section
  • page-attributes: The attributes box shown for pages. This is important for hierarchical post types, so you can select the parent post.


Provides a callback function that will be called when setting up the meta boxes for the edit form. The callback function takes one argument $post, which contains the WP_Post object for the currently edited post.

This feature is particularly useful to developers for them to create custom meta boxes that will show up in the CPT edit screen.

'register_meta_box_cb' => 'metabox_callback_func',


An array of registered taxonomies like category or post_tag that will be used with this custom post type.

'taxonomies' => array( 'post_tag', 'category '),


Setting this argument to true will enable archives for your custom post type. For example, say your CPT is books, visiting will display a list of posts belonging to books custom post type.


This argument allows you to define the permalink structure of your custom post type when viewing a single post or archive.

Default value is true and uses $post_type as slug. To prevent rewrites, set to false.

Let’s see some examples for clarity sake.

Say you created a review custom post type but wish to change the URL slug from review to assessment, using the following rewrite argument will change the URL from to for single posts and to for the CPT archive.

'rewrite' => array(
    'slug'       => 'assessment',
    'with_front' => false

Whenever you do a rewrite of WordPress URL, ensure you click the Save Changes button in Settings >> Permalinks to recreate the rewrite rules.

So basically, the slug defines the new URL slug while with_front determines if the permalink structure be pre-pended with the front base.

Still not clear on with_front? Let’s see an example.

Say you have a permalink structure with blog appended to the URL as depicted in the image below.

Custom Post Type - Common Settings

If with_front is set to false, the URL of a single post and post archive becomes and respectively but if it is set to true, the URL of a single post and post archive respectively becomes and

Notice the omission of blog in the latter? That’s the difference.


Use this argument to decide whether posts belonging to your custom post type can be exportable via the WordPress export tool. By default, this is set to true.


This argument allows you to control the query variable used to get posts of this type.

If set to true, it will allow you to request a book custom posts type via where harry-potter that is the URL slug of a book entry or post.

If set to a string rather than true (for example publication), you can do:

Caveat of “query_var”

If query_var is undefined in your CPT registration array argument, it defaults to $post_type thus, it is always defined unless you set it to false.

Here comes the caveat. Whenever the value of a query_var is added as a query string to a URL, it will lead to a 404.

Let me clarify. Say the value of your CPT query_var is review and a query string with the key set to review was added to any URL of your WordPress site in any of the following forms:

This will cause a 404 to happen.

I actually learned about this the hard way. As at the time I had this issue, I created a ticket in WordPress core trac to report it as a bug.

It took me weeks to finally figure it out before some members of the core WordPress team replied my ticket.

Speeding up Custom Post Types Setup with Plugins

Now that we’ve covered the foundations, it’s a good time to point out that there are a number of plugins in WordPress plugin repository that makes creating custom post types very easy.

Examples include (but are not limited to):


In this tutorial, we learned what custom post types are and how they’re created.

This is actually the first in a series on WordPress custom post types. In my next tutorial, we will learn how to customize the various admin notices, how to register custom taxonomies to a post type and adding contextual help tab to your CPT screen.

Until then, happy coding!

Frequently Asked Questions on WordPress Custom Post Types

What are the benefits of using WordPress custom post types?

WordPress custom post types offer a range of benefits. They allow you to create specific content types for your website, beyond the standard posts and pages. This can include portfolios, testimonials, products, and more. Custom post types also provide a more organized and efficient way to manage your content. They can be customized with specific features and fields, making it easier to input and display content in a consistent manner.

How do I create a custom post type in WordPress?

Creating a custom post type in WordPress involves adding a few lines of code to your theme’s functions.php file. You’ll need to use the register_post_type() function, which allows you to specify the name, labels, and other features of your custom post type. Remember to flush your rewrite rules after creating a new custom post type by visiting the Permalinks settings page.

Can I add custom fields to my custom post types?

Yes, you can add custom fields to your custom post types. This can be done using the add_meta_box() function, which allows you to add a new meta box to the post editing screen. You can then use this meta box to input and display additional information for your custom post type.

How do I display custom post types on my website?

Displaying custom post types on your website can be done by creating a new template file in your theme. This file should be named single-{post-type}.php, where {post-type} is the name of your custom post type. You can then use the standard WordPress loop to display your custom post type’s content.

Can I use plugins to create custom post types?

Yes, there are several plugins available that can simplify the process of creating custom post types. These plugins provide a user-friendly interface for creating and managing custom post types, without the need for coding. Some popular options include Custom Post Type UI, Pods, and Toolset Types.

How do I add taxonomies to my custom post types?

Adding taxonomies to your custom post types can be done using the register_taxonomy() function. This function allows you to create a new taxonomy, specify its labels, and associate it with your custom post type. You can then use this taxonomy to categorize and filter your custom post type’s content.

Can I create custom post types with hierarchical structures?

Yes, you can create custom post types with hierarchical structures. This can be done by setting the ‘hierarchical’ argument to true when registering your custom post type. Hierarchical custom post types can have parent and child posts, similar to pages.

How do I customize the admin interface for my custom post types?

Customizing the admin interface for your custom post types can be done using the ‘supports’ argument when registering your custom post type. This argument allows you to specify which features are available in the post editing screen, such as the title, editor, thumbnail, and custom fields.

Can I include custom post types in my website’s search results?

Yes, you can include custom post types in your website’s search results. This can be done by setting the ‘exclude_from_search’ argument to false when registering your custom post type. You can also use plugins or custom code to further customize your website’s search functionality.

How do I delete a custom post type in WordPress?

Deleting a custom post type in WordPress involves removing the code that registers the custom post type from your theme’s functions.php file. Remember to backup your website before making any changes to your theme’s code. After removing the code, you’ll need to flush your rewrite rules by visiting the Permalinks settings page.

Collins AgbonghamaCollins Agbonghama
View Author

Collins is a web developer and freelance writer. Creator of the popular ProfilePress and MailOptin WordPress plugins. When not wrangling with code, you can find him writing at his personal blog or on Twitter.

ChrisBCPTcustom post typecustom post typespost typesWordPress
Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week