WordPress has gone a long way from being a simple blogging platform to one of the most complex and widely used content management systems. It provides us with several post types by default: post, page, attachment, revision, navigation menu, etc. While these are useful, they are nearly not enough for the various other types of content a website might have. So if you want to extend your website functionality for custom data, it will be necessary to learn how to create a custom post type.

Whatever custom data you need to appear on your pages, with CPT you can easily implement it. The easiest way to identify the need for a new custom post type is to check if there is a deeply nested hierarchy within your default Pages content type. Forms in Contact Form 7 or products in WooCommerce are a common example for its application.

There are two ways to implement a custom post type in WordPress. The first one is to insert code directly in the functions.php file of a theme, which will do the job but it is not recommended. Your custom code will not work if you switch to other theme. Also, the main purpose of a functions.php file is for theme-specific functionality. Design and functionality should be clearly separated whenever possible. You should be able to switch themes without affecting functionality, and also be able to deactivate plugins without affecting the design.

The better way to include a new custom post type is to create a plugin. It is easier to disable, update, or reinstall if it is needed. Also, it is easier to manage as you know what each plugin does from its description.

To minimize the likelihood of a conflict with other theme or plugin you should always prefix your custom post type identifier.

Create custom post type

To create or modify a post type we use the function ‘register_post_type’.

register_post_type( $post_type, $args );

In the example below, we register custom post type ‘reviews’, which has an archive and is visible to authors and readers. Every argument is commented in order to explain what it does.

Example:

<?php
/*
Plugin Name: My Custom Post Types & Taxonomies
Description: Add custom post types and taxonomies
Author: Open4Tech
*/

function open4tech_custom_types() {
    register_post_type( 'reviews', // The name of the custom post type
        array(
            'labels' => array(
                'name' => __( 'Reviews' ), // A plural descriptive name for the post type
                'singular_name' => __( 'Review' ) // Name for one object of this post type
            ),
            'has_archive' => true, // Enables post type archives
            'public' => true, // Controls how the type is visible to authors and readers
            'rewrite' => array('slug' => 'reviews'), // Customize the permalink structure slug
            'show_in_rest' => true, // Enables REST API for Gutenberg
            'supports' => array('editor') // Registers support of certain feature(s) for a given post type
        )
    );
}
 
add_action( 'init', 'open4tech_custom_types' );

A slight code adjustment is needed because Gutenberg uses the REST API, which is turned off by default. The argument ‘show_in_rest’ must be set to true. Otherwise, Gutenberg is not applied to custom post types and they still use the classic editor. For a full list of the arguments that you can use with this function please check the WordPress Codex.

Create taxonomy

In WordPress, taxonomies are a way to group posts and custom post types together. Two very popular taxonomies are used on a regular basis: Categories and Tags. You may want to have a custom one for your new content type. It is done with ‘register_taxonomy’ function.

register_taxonomy( $taxonomy, $object_type, $args ); 

Example:

function open4tech_custom_taxonomies() {
	register_taxonomy('gadgets', // The name of the taxonomy
		array('reviews'), // Name of the object type for the taxonomy object (built-in Post Type or any Custom Post Type)
		array(
			'hierarchical' => true, // Is this taxonomy hierarchical (have descendants) like categories or not hierarchical like tags
			'label' => 'Gadgets', // A plural descriptive name for the taxonomy
			'singular_label' => 'Gadget', // Name for one object of this taxonomy
			'show_in_rest' => true, // Enables REST API for Gutenberg
			'rewrite' => array('slug' => 'gadgets') // Customize the permalink structure slug
		)
	);
}

add_action( 'init', 'open4tech_custom_taxonomies' ); 

In this example, the first parameter is the taxonomy name – ‘Gadgets’. The second is the name of the object type we’re applying it to, in our case the custom post type ‘reviews’ (which is an array). The taxonomy is hierarchical and can have descendants) like categories. The argument ‘show_in_rest’ must also be set to true in order to display the newly created taxonomy in Gutenberg. You can find a full list of arguments here.

We have manually added two categories in ‘Gadgets’ taxonomy to show how it works: ‘Modules’ and ‘Sensors’.

Both ‘register_post_type’ and ‘register_taxonomy’ functions should only be invoked through the ‘init’ action because they will not work if called before ‘init’, and aspects of the newly created or modified post type will work incorrectly if called later.