Web Development Tutorials

Programming

Register A Custom Post Type In WordPress

WordPress ships with posts and pages, but real projects need content of their own — books, products, events, portfolio items. You create these with a custom post type: a call to register_post_type(), hooked to the init action, that gives WordPress a new content type with its own admin menu, editor, and URLs. This tutorial registers a Book post type from a small plugin, wiring up its labels, a rewrite slug for pretty permalinks, and show_in_rest so it works in the block editor and the REST API.

Requirements:

  • WordPress 6.0 or newer (tested on WordPress 7.0.2).
  • Administrator access to install a plugin, or a child theme whose functions.php you can edit.
  • PHP 7.4 or newer — the version your WordPress already runs on.

How To Register a Custom Post Type in WordPress.

The objective is to add a “Books” content type that appears in the admin sidebar, opens the block editor with its own screen, and serves a public archive at /books/.

Step 1.

Create a plugin file so the code loads independently of your theme. In wp-content/plugins/, make a folder named book-post-type and, inside it, a file named book-post-type.php. The header comment is what makes WordPress list it on the Plugins screen.

<?php
/**
 * Plugin Name: Book Post Type
 * Description: Registers a "Book" custom post type.
 * Version:     1.0.0
 */

// Exit if accessed directly.
if (!defined('ABSPATH')) {
    exit;
}

You can instead paste the two functions below into your child theme’s functions.php — a plugin just keeps the content type alive when you switch themes.

Step 2.

Define the labels — the words WordPress shows around the admin UI — then hand them to register_post_type() together with the arguments that control how the type behaves. Add this below the header.

function ndriel_register_book_post_type() {

    // The text WordPress shows in menus, buttons, and messages.
    $labels = array(
        'name'          => 'Books',          // general (plural) name
        'singular_name' => 'Book',           // one item
        'menu_name'     => 'Books',          // the sidebar menu label
        'add_new_item'  => 'Add New Book',
        'edit_item'     => 'Edit Book',
        'view_item'     => 'View Book',
        'search_items'  => 'Search Books',
        'all_items'     => 'All Books',
        'not_found'     => 'No books found',
    );

    $args = array(
        'labels'        => $labels,
        'public'        => true,             // visible on the front end and in admin
        'has_archive'   => true,             // enable the /books/ archive page
        'rewrite'       => array('slug' => 'books'), // pretty permalinks
        'menu_icon'     => 'dashicons-book', // the sidebar icon
        'menu_position' => 5,               // just under Posts
        'supports'      => array('title', 'editor', 'thumbnail', 'excerpt'),
        'show_in_rest'  => true,             // block editor + REST API
    );

    register_post_type('book', $args);
}
add_action('init', 'ndriel_register_book_post_type');

The first argument to register_post_type()'book' — is the post type’s internal key. Keep it lowercase, under 20 characters, and never prefix it with wp_. Registering on the init hook is required: run it any earlier and WordPress is not ready; any later and rewrite rules and the admin menu miss it.

Step 3.

Activate the plugin under Plugins in wp-admin. A Books menu appears in the sidebar — with the book icon, an All Books list, and an Add New Book button — and it opens the same block editor you use for posts.

One gotcha: the /books/ archive returns 404 until the rewrite rules are rebuilt. Go to Settings → Permalinks and click Save Changes once (you need not change anything) to flush them. Do this only after adding a post or two so the archive has something to show.

Result.

Add a couple of books, publish them, and visit http://your-site/books/. WordPress serves the custom post type’s archive, listing each book with its excerpt and date — proof that the type is public, its archive is on, and the rewrite slug resolves:

GET /wp-json/wp/v2/types/book
{"name":"Books","slug":"book","rest_base":"book","has_archive":true,"hierarchical":false}

GET /wp-json/wp/v2/book?status=publish
[ {"title":"Clean Code",              "link":".../books/clean-code/"},
  {"title":"The Pragmatic Programmer","link":".../books/the-pragmatic-programmer/"} ]

The Books custom post type archive at /books/, titled 'Books Tutorials', listing two published books: Clean Code and The Pragmatic Programmer, each with its excerpt and date

Notes:

  • Always flush rewrite rules after adding or changing a rewrite slug, or the new URLs 404. Saving the Permalinks screen does it; in a plugin you can call flush_rewrite_rules() from an activation hook — never on every init, which is expensive.
  • Prefix your function names (here ndriel_) to avoid clashing with other plugins, but do not prefix the post type key with wp_ — those keys are reserved for core.
  • Set 'show_in_rest' => true (as above) so the type uses the block editor and is reachable at /wp-json/wp/v2/book. Without it you get the old classic editor and no REST endpoint.
  • Custom post types are not stored in your theme — register them in a plugin so switching themes never makes your content vanish from the admin.
  • To group books by subject, pair the type with a custom taxonomy via register_taxonomy(). That is the natural next step once the type exists.

References:

//

Featured tutorial

Leave a comment

Your email address will not be published. Required fields are marked *