WordPress Custom Post Type and register_post_type function Tutorial

📂 Category: PHP

🖺 Last modified: 7th Dec 2021

💻 Experience: Intermediate

🕑 Read Time: 10 min

Custom Post Types are among the most useful and commonly used features in WordPress. CPTs are widely used to manage and display different kinds of content that for some reason needs to be differentiated and separated from standard posts. In this tutorial we’ll show you how to create a WordPress Custom Post Type using the register_post_type function. You’ll also learn how to customize your CPTs and display them on your website.

Introduction to the WordPress Custom Post Type feature and register_post_type function

You probably noticed that certain themes have additional buttons in the WP Dashboard menu such as Testimonials, Team Members, Portfolio, Counters, Products, etc. Those tabs are actually Custom Post Types. Technically, CPTs are just like regular posts or pages, but customized to display content that doesn’t fit the regular post pattern. CPTs are also used to separate specific content type from regular posts and pages inside the WP Dashboard. WordPress Custom Post Type can be used in for a wide variety of purposes depending on specific project needs.

Creating a WordPress Custom Post Type using the register_post_type function is a quite simple process, yet some development experience is still required. We’ll assume you’re familiar with creating child themes, editing template files and WordPress functions. At least basic understanding of PHP will also come handy to customize the code in this tutorial according to your needs.

The process of creating a WordPress Custom Post Type consists of the following steps:

  1. registering a CPT using the register_post_type function
  2. customizing the register_post_type function
  3. adding custom fields and meta boxes to the CPT post editor
  4. creating the template file to display the CPT post
  5. creating the CPT post loop template to display the archive of custom posts
  6. displaying WordPress Custom Post Type content on the website front end

NOTE: Number of steps and their complexity may vary form project to project depending on it’s specific needs.


WordPress Custom Post Type and register_post_type function essentials

To make it easier for you to understand how to create a WordPress Custom Post Type, firstly we’ll analyze the essential code needed to make it work. In the following example we’ll register a new CPT using the register_post_type function and display some CPT content on the website. We won’t customize the register_post_type function, neither create CPT post templates at this moment.

Let’s say we run a Formula 1 racing website and we want to use a WordPress Custom Post Type to display race results. To create our CPT, firstly we have to register it by adding the register_post_type function to our theme’s functions.php file:

function custom_posttypes() {
    register_post_type( 'races',
            'labels' => array(
            'name' => __( 'Races' ),
            'singular_name' => __( 'Race' )
            'public' => true,
            'rewrite' => array('slug' => 'results '),
            'has_archive' => true,
            'show_in_rest' => true

add_action( 'init', 'custom_posttypes' );

Firstly we created a function called custom_posttypes() which we’ll use to register and configure our CPT. You can name this function whatever you like. It contains the register_post_type function with the essential array of options needed for the CPT to work properly. We named our custom post type races and added two essential labels to it. Other essential parameters are ‘public’ => true (to make CPT content visible on the front end), ‘rewrite’ => array(‘slug’ => ‘results’) (the slug we’ll use to display CPT’s archive) ,‘has_archive’ => true (to enable displaying the CPT archive) and ‘show_in_rest’ => true (to enable Gutenberg editor support). Finally, we’ll hook up our custom_posttypes() function to our theme using the add_action() function.

If you’ve done everything correctly, after refreshing your WP Dashboard, you’ll notice a new button called Races inside the left colum menu. Similarly to regular posts, you have the options to see your CPT post list or add new posts. Now it’s the right time to add some content to your CPT. For purposes of this tutorial, we added a couple of race results to our WordPress Custom Post Type.

WordPress Custom Post Type in WP Dashboard

When a WordPress Custom Post Type is properly registred using the register_post_type function, it gets it’s own section within the WP Dashboard.


Displaying WordPress Custom Post Type content on the website front end

Now that you successfully registered your CPT using the register_post_type function and added some content to it, it’s time to display that content on your website’s front end. We’re still showing you only the essential steps to create a WordPress Custom Post Type, so we won’t significantly customize the CPT display at this moment.

The simplest and fastest way to see the posts you added to your CPT is using the WordPress archive template. Since we haven’t created a custom archive template yet, WordPress will use it’s default archive template archive.php to display CPT posts. To display your WordPress Custom Post Type content this way, follow this procedure:

  • make sure to set your permalinks to use Post Name as slug (WP Dashboard Settings → Permalinks)
  • in your browser, enter the url containing your website’s address followed by the slug you defined in the register_post_type function, for example https://yourwebsite.com/results/
  • optionally, you can create a Menu Item by adding the CPT archive url to the Custom Linksfield in the WP Menu editor

The other way of displaying WordPress Custom Post Type content is creating a post loop query. You can add the the CPT post loop query anywhere inside your template files, where you want the CPT posts to appear. For testing & learning purposes, you can add the following code inside the index.php file, right before the opening <footer> tag or before the get_footer() function (depending on the theme structure):

<section id="race-results">
<h2>Race Results</h2>
$args = array( 'post_type' => 'races' );
$races = new WP_Query( $args );
    if( $races->have_posts() ) {
    while( $races->have_posts() ) {
    <div class='single-cpt'>
    <h1><?php the_title() ?></h1>
    <?php the_content() ?>
    <?php  } }
    else {  echo 'No race results to display.';  }

This very simple post loop queries all posts belonging to the races custom post type. Of course, the post loop is adjusted to our example website showing F1 race results. You can adjust it according to your needs and use your own post_type argument you defined in the register_post_type function earlier. At this moment we won’t customize the post loop display any further.

If you’ve done everything correctly, the posts you added to your WordPress Custom Post Type should appear on your website’s homepage, between the content section and the footer.

WordPress Custom Post Type loop on frontend.

CPT posts or post loops can be displayed on the website frontend using the default Archive Template or by creating a custom post query or template.


Customizing the register_post_type function

Previously in this tutorial we showed you the essential steps in creating a WordPress Custom Post Type. Now is the time to move forward and learn how to create more complex CPTs, the kind you’ll probably use for your project. Firstly, let’s customize the register_post_type function by adding more variables and arguments to their array.

Copy the following code into yout theme’s functions.php file (or replace the code from the previous example):

function custom_posttypes() {
$ui_fields = array(
'title', 'editor', 'thumbnail', 'excerpt', 'custom-fields',
 'revisions', 'post-formats', 'author', 'comments'
$ui_labels = array(
'name' => _x('Races', 'plural'),
'singular_name' => _x('Race', 'singular'),
'menu_name' => _x('Races', 'admin menu'),
'name_admin_bar' => _x('Race', 'admin bar'),
'all_items' => __('All Races'),
'add_new' => _x('Add New', 'add new'),
'add_new_item' => __('Add New Race'),
'new_item' => __('New Race'),
'edit_item' => __('Edit Race'),
'view_item' => __('View Race'),
'view_items' => __('View Races'),
'search_items' => __('Search Races'),
'not_found' => __('No races found.')
$args = array(
'supports' => $ui_fields,
'labels' => $ui_labels,
'public' => true,
'menu_position'  => 20,
'query_var' => true,
'rewrite' => array('slug' => 'results'),
'has_archive' => true,
'hierarchical' => false,
'show_in_rest' => true
register_post_type('races', $args);
add_action('init', 'custom_posttypes');

In the first part of the code we defined which user interface elements will be used within our WordPress Custom Post Type editor. We created a variable called $ui_fields and added parameters to it’s array that will enable all standard editor fields. You can customize the array according to your needs by removing or adding parameters. To learn more about the parameters you can use to customize the CPT editor user interface, consult the WordPress Code Reference post_type_supports section.

In the second part of the code we customized the standard user interface labels. Those labels appear in various positions inside the WP Dashboard, such as the admin bar, left column menu, CPT list view and the CPT editor itself. Similarly to customizing the UI elements, we created a variable called $ui_labels and customized the standard set of user interface labels. More information about which UI labels can be customized can be found in the WP Code Reference get_post_type_labels section.

The final part of the code is similar to our first basic example of creating a WordPress Custom Post Type. As in the first example, we defined the array of arguments needed to register the CPT. We also included our arrays of user interface customization parameters into the arguments array. In our example we used only a couple of most frequent arguments. The full list of arguments that can be used to define a CPT can be found within the WordPress Code Reference register_post_type section.

NOTE: You can define the position of your WordPress Custom Post Type Tab inside the WP Dashboard left column Menu. It’s defined by setting a value for the ‘menu_position’ option. Here’s a short guide on how to set the ‘menu_position’ option value and define the position of the CPT Tab:

  •  5 – after Posts
  • 10 – after Media
  • 15 – after Links
  • 20 – after Pages
  • 25 – after Comments
  • 60 – after first separator
  • 65 – after Plugins
  • 70 – after Users
  • 75 – after Tools
  • 80 – after Settings
  • 100 – after second separator


WordPress Custom Post Type taxonomies: Categories & Tags

Same as regular posts, CPT posts can also be grouped by taxonomies: categories and tags. By default, a WordPress Custom Post Type created using the register_post_type function doesn’t have taxonomies functionality enabled. To start using taxonomies with your CPT posts, firstly you have to enable them using the register_taxonomy() function. The taxonomy registration process is quite similar to registering CPTs the way we showed earlier in this tutorial.

We’ll add the following code to our functions.php file to enable taxonomies in our F1 Races Custom Post Type example:

function cpt_taxonomies() {
  $labels = array(
    'name'              => _x( 'Race Categories', 'taxonomy general name' ),
    'singular_name'     => _x( 'Race Category', 'taxonomy singular name' ),
    'menu_name'         => __( 'Race Categories' ),
    'all_items'         => __( 'All Race Categories' ),
    'search_items'      => __( 'Search Race Categories' ),    
    'parent_item'       => __( 'Parent Race Category' ),
    'parent_item_colon' => __( 'Parent Race Category:' ),
    'edit_item'         => __( 'Edit Race Category' ), 
	'add_new_item'      => __( 'Add New Race Category' ),
    'new_item_name'     => __( 'New Race Category' ),
    'update_item'       => __( 'Update Race Category' )  
  $args = array(
    'labels' => $labels,
    'hierarchical' => true,
    'show_in_rest' => true,
 register_taxonomy( 'race_category', 'races', $args ); }

add_action( 'init', 'cpt_taxonomies', 0 );

Similarly to registering CPT’s, the registration of CPT taxonomies consists of creating a function, customizing UI labels and creating an array functionality of settings. Furthermore, we have to register the taxonomy using the register_taxonomy() function and append it to our custom post type ‘races’. Finally, we hook the function to our theme using the add_action() function.

In the taxonomy registration process, the ‘hierarchical’ option inside the $args is used to define if the CPT taxonomy will behave like a category or a tag:

  • ‘hierarchical’ => true – taxonomy behave as category
  • ‘hierarchical’ => false – taxonomy behave as tag

NOTE: ‘show_in_rest’ => true is used to activate taxonomy support inside the Gutenberg editor. If set to false, you won’t see the category or tag selection box inside the post editor. It will be available only within the post list Quick Edit function.

WordPress Custom Post Type Taxonomies Enabled frontend.

When enabled, WordPress Custom Post Type Taxonomies look and behave exactly as regular post taxonomies. Notice the customized User Interface labels.


Adding and displaying Custom Fields in WordPress CPT

Since a WordPress Custom Post Type is primary used to create customized posts different than the standard post format, you’ll probably want to add the possibility to enter and display some additional information. The simplest way to do it are the Custom Fields. In the previous customized register_post_type function example, we enabled Custom Fields inside the $ui_fields array. According to our F1 Racing tutorial topic, inside the CPT editor we’ll create a Custom Field named “Race Date” and add a date to it.

To show our Custom Field value inside the CPT post loop on the website front end, we need to slightly modify the code we used earlier to display our custom posts:

/* CUT */
<div class='single-cpt'>
<h1><?php the_title() ?></h1>
<p>Race date: <?php echo get_post_meta($post->ID, 'Race Date', true); ?></p>
<?php the_content() ?>
/* CUT */

The code modification consists of adding the get_post_meta() function to our WordPress Custom Post Type loop, where we want the additional information to appear. In our example we added it between the post title (Track name) and the post content (Race results). If you’re not familiar with Custom Fields in WordPress, you can learn more about that topic in our Custom Fields Tutorial.


Learn more about WP CPTs

We hope this tutorial gave you the insight into how WordPress Custom Post Type work and how to create your own CPTs using the register_post_type function. As you already figured, the CPT possibilities are quite extensive. To learn more about what can be done using custom post types, the best place to start expanding your knowledge is the WordPress Code Reference.