We’ve already established that everything in WordPress is a post. They differ by their post type. There are over a dozen post types available in WordPress by default (as of July 2025), but the real magic comes from custom post types. Here’s a list of some of the most important default WordPress post types:
- Posts
- Pages
- Attachments
- Revisions
- Navigation Menus
- Block templates
- Block template parts
Let’s assume we’re creating a website for a library. We need a way to display books. The best way to achieve that would be to create a custom post type ‘book’. But why? Why not just use the default post with a ‘Books’ category? Here’s why custom post types are so amazing:
- A separate admin tab – books will get their own tab in the admin menu instead of being mixed with blog posts.
- Custom fields – a book needs different data than a simple post. It has the author, release date, number of pages, number of copies sold, etc. You can add custom meta boxes specifically for inputting content like that. You can even add image galleries or date pickers. You can then use this data in the book’s template.
- More control over templates – if you looked at the post’s template hierarchy, you would see that there’s no way for us to create a separate template for posts of different categories. But you can create a template for a different post type. In our case, we could create a single-book.php template, which would display the single book page (we don’t want it to look like a simple post, do we?).
- Semantic correctness – a book is not a blog post. You wouldn’t treat pages with books the same way you treat blog posts. They are semantically different, and as such, they should be structurally separate. The most obvious example is the posts archive, which you would create with the home.php file. Do you really want books to show up among your posts?
- Custom permalinks structure – a separate post type gets its own permalinks structure. Instead of having /category/books/book-title, you can have /books/book-title.
So how do you register a custom post type? You use the register_post_type() function. Let’s register our ‘book’ post type:
function register_book_post_type() {
$labels = [
'name' => 'Books',
'singular_name' => 'Book',
'menu_name' => 'Books',
'add_new_item' => 'Add New Book',
'edit_item' => 'Edit Book',
// ... other labels
];
$args = [
'labels' => $labels,
'public' => true,
'hierarchical' => false,
'has_archive' => true,
'menu_icon' => 'dashicons-book',
'supports' => [ 'title', 'editor', 'thumbnail', 'excerpt', 'custom-fields' ],
'taxonomies' => [ 'genre' ],
'rewrite' => [ 'slug' => 'books' ],
];
// The first argument is the unique name
// (max 20 chars, no spaces/caps, should be prefixed with a
// unique theme/plugin identifier to avoid conflicts - 'thm' here for "theme")
register_post_type( 'thm_book', $args );
}
add_action( 'init', 'register_book_post_type' );This list of arguments is not exhaustive, and I’m not going to explain them. RTFM.
Your custom post types should be registered in a plugin, not a theme, as themes are supposed to be responsible for presentation only (more on that later). This rule is often bent, especially if the website in question is not likely to be migrated to a different theme. You will see custom post types registered in functions.php a lot.