Skip to content

PHP Tutorial PHP for WordPress Developers

Custom Post Type Basics

Lesson 335 of 369 2 min read By Deepak Paswan Updated Oct 5, 2026

WordPress has built-in content types: posts and pages. A custom post type (CPT) is a new kind of content with its own admin menu and URLs: products, events, recipes, courses, portfolio items.

Register one

index.php
PHP
<?php
add_action("init", function (): void {
    register_post_type("recipe", [
        "labels" => [
            "name" => "Recipes",
            "singular_name" => "Recipe",
            "add_new_item" => "Add New Recipe",
        ],
        "public" => true,
        "has_archive" => true,
        "show_in_rest" => true,                       // block editor and REST API
        "menu_icon" => "dashicons-carrot",
        "supports" => ["title", "editor", "thumbnail", "excerpt"],
        "rewrite" => ["slug" => "recipes"],
    ]);
});

do_action("init");   // in a real plugin WordPress fires this for you

var_dump(post_type_exists("recipe"));
echo get_post_type_object("recipe")->labels->singular_name;
Output
OUTPUT
bool(true)
Recipe

(The do_action("init") call here only makes the demo run on its own. In a real plugin you do not call it. WordPress runs init once per request.)

The important arguments

ArgumentMeaning
labelsNames shown in the admin
publicVisible in the admin and on the front end
has_archiveCreates an archive page (/recipes/)
show_in_restRequired for the block editor and the REST API
supportsFeatures: title, editor, thumbnail, excerpt, custom-fields…
rewriteThe URL base (/recipes/chocolate-cake/)
menu_iconA Dashicons name or an image URL
capability_typeWhich permissions apply (default post)
hierarchicaltrue for page-like parent and child items

Naming rules

  • The post type key is lowercase, up to 20 characters, with letters, numbers, underscores and dashes.
  • Prefix it if there is a risk of a clash (myplugin_recipe).
  • Do not use reserved names such as post, page, attachment, revision, nav_menu_item.

Register on init

CPTs must be registered on the init hook, early enough that WordPress knows about them when it parses the URL.

Flush rewrite rules, but only once

Adding a post type with a new URL slug needs WordPress to rebuild its URL rules. Do this only when the plugin is activated, never on every request (it is slow).

index.php
PHP
<?php
register_activation_hook(__FILE__, function (): void {
    my_register_recipe_post_type();   // register first
    flush_rewrite_rules();
});

register_deactivation_hook(__FILE__, "flush_rewrite_rules");

In development, you can refresh the rules by visiting Settings, Permalinks and pressing Save.

A custom taxonomy

A taxonomy groups content (like categories and tags).

index.php
PHP
<?php
add_action("init", function (): void {
    register_taxonomy("cuisine", ["recipe"], [
        "labels" => ["name" => "Cuisines", "singular_name" => "Cuisine"],
        "hierarchical" => true,
        "show_in_rest" => true,
        "rewrite" => ["slug" => "cuisine"],
    ]);
});

do_action("init");

var_dump(taxonomy_exists("cuisine"));
Output
OUTPUT
bool(true)

Display them: query the CPT

index.php
PHP
<?php
$recipes = new WP_Query(["post_type" => "recipe", "posts_per_page" => 6]);

if ($recipes->have_posts()) :
    while ($recipes->have_posts()) : $recipes->the_post();
        the_title("<h2>", "</h2>");
    endwhile;
    wp_reset_postdata();
endif;

WordPress looks for single-recipe.php and archive-recipe.php in the theme (see WordPress Template PHP).

Custom fields

For extra data (cooking time, price), use post meta. Plugins like Advanced Custom Fields give you a friendly UI for it. In code: get_post_meta() and update_post_meta() (see WordPress Functions).

CPT in a plugin, not in a theme

If the content should survive when the site changes theme, register the post type in a plugin. A theme-registered CPT disappears when the theme is switched.

info
The same tool for every content type
WooCommerce products, and the course lessons you are reading, are custom post types. register_post_type() is how WordPress turns into a content management system for anything.