Home About Skills Products Work
Projects Services Experience
Learn
Tutorials Courses Blogs Resources
Contact
Blog · WordPress

How to Register a Custom Post Type in WordPress (Step-by-Step)

WordPress's built-in "Posts" are fine for a blog, but the moment you need "Products," "Testimonials," or "Team Members" with their own distinct fields and archive pages, you need a custom post type. Here's exactly how to register one properly.

The register_post_type() Function

Add this to your theme's functions.php (or, better, a dedicated plugin so it survives a theme switch):

function register_testimonial_post_type() {
    register_post_type('testimonial', [
        'labels' => [
            'name' => 'Testimonials',
            'singular_name' => 'Testimonial',
            'add_new_item' => 'Add New Testimonial',
        ],
        'public' => true,
        'has_archive' => true,
        'supports' => ['title', 'editor', 'thumbnail'],
        'menu_icon' => 'dashicons-format-quote',
        'rewrite' => ['slug' => 'testimonials'],
    ]);
}
add_action('init', 'register_testimonial_post_type');

Why the init Hook Specifically

Post types must be registered on every page load, on the init hook — not conditionally, not only in the admin. WordPress needs to know about the post type on every single request to correctly route URLs and build the admin menu.

Flushing Rewrite Rules

After adding a new post type, its archive/single URLs return a 404 until WordPress regenerates its rewrite rules. Visit Settings → Permalinks and click Save once (this is a one-time step, not something to run on every page load — flushing rewrite rules on every request is a well-known performance killer).

Making It Public in Search and Menus

'publicly_queryable' => true,
'show_in_nav_menus' => true,
'show_in_rest' => true, // required for Gutenberg editor + REST API access

Querying Your Custom Post Type

$testimonials = new WP_Query([
    'post_type' => 'testimonial',
    'posts_per_page' => 6,
]);

while ($testimonials->have_posts()) : $testimonials->the_post();
    the_title();
    the_content();
endwhile;
wp_reset_postdata();

Custom post types are the foundation almost every real WordPress build sits on — the moment content stops being "just a blog post," this is the tool for the job.

Getting Started with Advanced Custom Fields (ACF) in WordPress

Getting Started with Advanced Custom Fields (ACF) in WordPress

Field groups, repeaters, displaying ACF data in templates, and registering fields in code so they ship with your theme.

Writing Custom Functions in WordPress: A Beginner's Guide to functions.php

Writing Custom Functions in WordPress: A Beginner's Guide to functions.php

Actions vs filters, safe functions.php structure, and the defensive habits that keep a single typo from taking down your site.

WordPress for Developers: Hooks, Actions, and Filters Explained

WordPress for Developers: Hooks, Actions, and Filters Explained

The mental model behind every WordPress plugin and theme — and how to create your own hooks so others can extend your code too.

Esc