Knowing what post types and custom taxonomies are is one of the easiest ways to tell whether someone has really started learning WordPress development. Being able to create custom post types and build themes or plugins on top of them is one of the milestones on the road to becoming a more advanced WordPress developer.
This article is a practical guide to registering WordPress custom post types with register_post_type(). Once you understand the ideas here, you usually do not need to keep searching for smaller scattered tutorials.
Here is a look at one of the custom post types we used inside a WordPress CRM system.

What a custom post type really is
This is an important question. Once you understand what a custom post type actually is, the rest of the API makes much more sense. Technically, a custom post type is represented by a class called WP_Post_Type. When WordPress registers a post type, it creates an object of that class.
In normal development we do not instantiate WP_Post_Type directly. Instead, we use register_post_type(), which handles the object creation and registration for us.
First, look at the parameters supported by register_post_type()
The function supports a large set of parameters. Once you understand how to shape those arguments, you can register a post type that fits your needs very precisely.
<?php
// Register custom post types on init.
add_action( 'init', 'my_register_post_types' );
/**
* Register the post types used by the plugin.
*
* @since 1.0.0
* @access public
* @return void
*/
function my_register_post_types() {
$args = [
// Description, labels, public visibility, rewrite rules,
// supports, menu position, and all the other post type settings.
];
register_post_type( 'order', $args );
}
Create custom post types with visual tools
If you prefer a UI-driven workflow, there are tools that can generate custom post type code for you. Popular examples include the Custom Post Type UI plugin and GenerateWP’s Post Type Generator service.
Modify already-registered post types with the register_post_type_args filter
The example above covers creating a brand-new post type. But sometimes you need to change a post type that has already been registered by a theme, plugin, or earlier part of the code. WordPress provides the register_post_type_args filter for that purpose.
In the example below, the order post type is modified so that its public setting becomes false, which effectively hides it from public use.
add_filter( 'register_post_type_args', function ( $args, $post_type ) {
if ( 'order' === $post_type ) {
$args['public'] = false;
}
return $args;
}, 10, 2 );
Use helper functions to change a custom post type
Besides filtering the arguments directly, WordPress also provides helper functions for several common post type modification tasks.
add_post_type_support( 'post_type_name', [ 'title', 'editor' ] ); // Add support for features.
register_taxonomy_for_object_type( 'taxonomy_name', 'post_type_name' ); // Attach a taxonomy.
unregister_taxonomy_for_object_type( 'taxonomy_name', 'post_type_name' ); // Remove a taxonomy.
Unregister or remove an existing custom post type
If registration exists, then removal should exist too. WordPress lets you remove an existing post type with the following function:
unregister_post_type( 'post_type_name' );
Useful things to do with custom post types
Once you have custom post types, you will usually want to inspect or work with them in code. The following helpers are useful when checking capabilities and retrieving information about post types.
Check the nature of a post type
is_post_type_hierarchical( $post_type ); // Is it hierarchical?
post_type_exists( $post_type ); // Does it exist?
post_type_supports( $post_type, $feature );// Does it support a feature?
is_post_type_viewable( $post_type ); // Is it viewable on the front end?
Get post type objects and properties
get_post_type( $post = null ); // Get the post type of a post.
get_post_type_object( $post_type ); // Get the post type object.
get_post_types( $args = [], $output = 'names', $operator = 'and' ); // Get all post types.
get_post_type_labels( $post_type_object ); // Get labels.
get_all_post_type_supports( $post_type ); // Get all supported features.
How did I learn all of this?
To put together the material above, I mainly pulled information from the following three places:
- Documentation: https://codex.wordpress.org/Function_Reference/register_post_type
- Source code:
/wp-includes/post.php - Source code:
/wp-includes/class-wp-post-type.php
