Get 50% Discount Offer 26 Days

Recommended Services
Supported Scripts
WordPress
Hubspot
Joomla
Drupal
Wix
Shopify
Magento
Typeo3
How to Create Custom Post Type in WordPress and Integrate It with Rewrite Rules, REST API, and UI

Custom Post Type in WordPress lets you go beyond standard posts and pages to create structured content tailored to your site’s specific needs—like portfolios, testimonials, events, or products. By carefully configuring rewrite rules, enabling the REST API, and ensuring a good user interface (UI) in the WordPress admin, you make your custom post types feel like a natural part of WordPress.

In this article, we will walk you through how to create a custom post type programmatically, integrate it with WordPress rewrite rules, ensure it works seamlessly with the REST API (for block editor and external applications), and make it user-friendly in the WordPress dashboard. You’ll learn best practices, see code examples, and get tips on avoiding common pitfalls.

Introduction to Custom Post Type in WordPress

By default, WordPress includes several post types, such as post and page. However, not all content fits neatly into these categories. For instance, if you’re running a movie review site, you might want a “Movie” post type. If you manage an online directory, you might need a “Listing” post type. Custom Post Types let you structure your content for better organization, display, and management.

Defining your own CPT is as simple as calling the register_post_type() function, usually hooked to the init action. Once registered, your CPT can appear in the WordPress admin menu, be queryable on the front end, have its custom capabilities, and be integrated into the site’s permalink structure.

Key Steps to Creating and Integrating a Custom Post Type

  1. Register the Post Type:
    Use register_post_type() in a function hooked to init. It ensures that the CPT is registered after WordPress core is loaded but before the final output is sent to the browser.
  2. Set Rewrite Rules Properly:
    By specifying rewrite arguments, you control how URLs are formed. Combine this with flush_rewrite_rules() on activation/deactivation of your plugin (or theme setup) to ensure your nice permalinks work as intended.
  3. Enable show_in_rest for REST Integration:
    Setting show_in_rest => true allows your CPT to integrate with the WordPress REST API. It is crucial for using the Gutenberg block editor and for external applications or headless WordPress setups.
  4. Configure UI Options:
    Define labels, menu icons, and supports (like title, editor, thumbnail) to ensure the CPT feels at home in the WordPress admin interface.
  5. Flush Rewrite Rules on Activation/Deactivation:
    When you first register a CPT, you might need to refresh permalinks to activate the new rewrite rules. Doing this programmatically upon plugin activation/deactivation ensures a seamless experience for administrators.
Key Steps to Creating and Integrating a Custom Post Type in WordPress

Example Code to Register a Custom Post Type

Below is a basic code snippet demonstrating how to register a CPT called my_custom_post. In a real scenario, you would place this code in a plugin file or your theme’s functions.php file. Using a plugin is often considered best practice, as it keeps functionality separate from the theme.

<?php

/**

 * Plugin Name: My Custom Post Type Plugin

 * Description: Registers a custom post type "My Custom Post".

 */

// Register the CPT on init

add_action( 'init', 'myplugin_register_my_custom_post_type' );

function myplugin_register_my_custom_post_type() {

    register_post_type( 'my_custom_post', [

        'label' => __( 'My Custom Posts', 'textdomain' ),

        'public' => true,

        'show_in_rest' => true, // Enable REST API support

        'rewrite' => [ 'slug' => 'custom' ],

        'supports' => [ 'title', 'editor', 'thumbnail', 'custom-fields' ],

        'menu_icon' => 'dashicons-admin-post',

        'menu_position' => 5,

        'has_archive' => true,

        'labels' => [

            'name' => __( 'My Custom Posts', 'textdomain' ),

            'singular_name' => __( 'My Custom Post', 'textdomain' ),

            'add_new_item' => __( 'Add New Custom Post', 'textdomain' ),

            'edit_item' => __( 'Edit Custom Post', 'textdomain' ),

            'new_item' => __( 'New Custom Post', 'textdomain' ),

            'view_item' => __( 'View Custom Post', 'textdomain' ),

            'search_items' => __( 'Search Custom Posts', 'textdomain' ),

            'not_found' => __( 'No custom posts found.', 'textdomain' ),

            'not_found_in_trash' => __( 'No custom posts found in Trash.', 'textdomain' ),

        ],

    ] );

}

In this code:

  • public => true: Makes the CPT visible on the front end.
  • show_in_rest => true: Integrates the CPT with the REST API and Gutenberg editor.
  • rewrite => [ ‘slug’ => ‘custom’ ]: Sets the permalink base to example.com/custom/.
  • Supports: Defines which features this post type supports (like title, editor, thumbnail).
PHP code to register WordPress custom post type with REST API support

Ensuring Rewrite Rules Work

If you visit your new custom post type’s single post page and see a 404 error, don’t panic. You might need to flush rewrite rules. The easiest manual method is going to Settings > Permalinks in WordPress and clicking “Save Changes” without modifying anything. This process refreshes the rewrite rules.

To do it programmatically, use flush_rewrite_rules() on plugin activation and deactivation:

register_activation_hook( __FILE__, 'myplugin_activate' );

register_deactivation_hook( __FILE__, 'myplugin_deactivate' );

function myplugin_activate() {

    myplugin_register_my_custom_post_type();

    flush_rewrite_rules();

}

function myplugin_deactivate() {

    flush_rewrite_rules();

}

Important: Don’t call flush_rewrite_rules() on every page load or after the init hook each time—this can harm performance. Instead, do it once upon activation or after changes to rewrite rules.

PHP code for WordPress activation and deactivation hooks with flush rules

Integrating with the REST API

show_in_rest => true is your entry ticket to the WordPress REST API. This flag makes your custom post type accessible via endpoints like:

https://example.com/wp-json/wp/v2/my_custom_post

This integration is crucial if you:

  • Use the Gutenberg block editor, which relies on the REST API.
  • Build a headless WordPress site where a JavaScript framework like React or Vue handles the front end.
  • Need to retrieve or update your custom post type’s content from external applications or services.

Additional REST API customization options:

  • rest_base: Change the endpoint slug.
  • rest_controller_class: Use a custom controller for advanced functionality.

For most cases, show_in_rest => true is enough, but the REST API is highly customizable if you need more control.

Crafting a Great UI Experience

Part of properly integrating your CPT is making sure it’s easy for administrators to use. Consider the following:

  1. Labels:
    Good labels help users understand what your CPT represents. Use meaningful names and translations if you’re targeting an international audience.
  2. Menu Icons and Positions:
    The menu_icon argument allows you to choose a Dashicon or a custom icon to represent your CPT. Placing it in a sensible menu_position ensures a logical order in the admin menu.
  3. Supports Array:
    Decide what editing features your CPT needs. If you’re making a simple announcement post type, maybe the title and editor are enough. For something more complex like a product, you might want custom-fields, thumbnail, excerpt, or even comments.

Taxonomies:
If your CPT should be organized by categories, tags, or custom taxonomies, register those taxonomies and associate them with the CPT. For example:

register_taxonomy( 'custom_category', 'my_custom_post', [

    'label' => __( 'Custom Categories', 'textdomain' ),

    'hierarchical' => true,

    'public' => true,

    'rewrite' => [ 'slug' => 'custom-category' ],

    'show_in_rest' => true,

] );
  1. It creates a taxonomy called “Custom Categories” that you can apply to your CPT items.
PHP code to register custom taxonomy 'Custom Categories' in WordPress

Ensuring Compatibility and Best Practices

  1. Namespace and Text Domain:
    Use a unique slug for your CPT that won’t conflict with other post types. Also, use a proper textdomain for translations. For example, if your plugin is named “My Plugin,” use textdomain => my-plugin.
  2. Capabilities and Permissions:
    If you need fine-grained control over who can edit your CPT, define custom capabilities. For example, you can set capability_type => ‘post’ or define your own capabilities array to control editing, deleting, and publishing by user role.
  3. Performance Considerations:
    Registering a CPT is generally not resource-heavy. Just be mindful not to call register_post_type() multiple times unnecessarily. Hook it on init once. Also, avoid flushing rewrite rules repeatedly, as mentioned earlier.
  4. Version Control and Deployment:
    Consider placing your CPT code in a plugin rather than a theme so you maintain content structure regardless of theme changes. It ensures your CPT data remains intact if you switch themes.

Example of a More Complex CPT Registration

Let’s expand the previous example to include a taxonomy, custom capabilities, and a more extensive set of arguments:

add_action( 'init', 'myplugin_register_movie_cpt' );

function myplugin_register_movie_cpt() {

    // Register a taxonomy for genres

    register_taxonomy( 'genre', 'movie', [

        'label' => __( 'Genres', 'my-plugin' ),

        'public' => true,

        'hierarchical' => true,

        'rewrite' => [ 'slug' => 'genre' ],

        'show_in_rest' => true,

    ] );

    // Register the movie CPT

    register_post_type( 'movie', [

        'label' => __( 'Movies', 'my-plugin' ),

        'public' => true,

        'show_in_rest' => true,

        'rewrite' => [ 'slug' => 'movies' ],

        'menu_icon' => 'dashicons-format-video',

        'has_archive' => true,

        'supports' => [ 'title', 'editor', 'thumbnail', 'excerpt', 'custom-fields' ],

        'capability_type' => 'post', // or define custom capabilities for more control

        'taxonomies' => [ 'genre' ],

        'labels' => [

            'name' => __( 'Movies', 'my-plugin' ),

            'singular_name' => __( 'Movie', 'my-plugin' ),

            'add_new_item' => __( 'Add New Movie', 'my-plugin' ),

            'edit_item' => __( 'Edit Movie', 'my-plugin' ),

            'new_item' => __( 'New Movie', 'my-plugin' ),

            'view_item' => __( 'View Movie', 'my-plugin' ),

            'search_items' => __( 'Search Movies', 'my-plugin' ),

            'not_found' => __( 'No movies found.', 'my-plugin' ),

            'not_found_in_trash' => __( 'No movies found in Trash.', 'my-plugin' ),

        ],

    ] );

}

This CPT, named “movie,” comes with a “genre” taxonomy, sets a custom slug (movies), supports a variety of features, and uses the REST API. It will appear in the WordPress admin with a video icon and a standard set of labels.

PHP code registering custom post type and taxonomy for WordPress movies

Troubleshooting Common Issues

  • 404 Errors on Single CPT Pages:
    If you get a 404 error when visiting single CPT items, try flushing rewrite rules. Go to Settings > Permalinks and click “Save Changes,” or run flush_rewrite_rules() once in your plugin’s activation hook.
  • CPT Not Appearing in Admin Menu:
    Check that public => true or show_ui => true. If the public is false, also ensure show_ui is set to true. With these, you might see the CPT in the admin sidebar.
  • REST API Endpoint Not Found:
    Verify show_in_rest => true. Also, ensure you’re using WordPress 4.7 or higher (as older versions might not fully support REST without the JSON plugin). If you set a custom rest_base, check the endpoint in the format: wp-json/wp/v2/{rest_base}.
  • CPT Not Editable in Gutenberg:
    The Gutenberg editor relies on the REST API. Make sure show_in_rest => true, and you have the necessary capabilities and support. If you disabled editor support, Gutenberg won’t load. You can re-enable it in support.
  • Clashing Slugs:
    If another post type or taxonomy already uses your chosen slug, pick a unique slug. Using generic slugs like “content” or “news” can sometimes conflict with existing rewrites. Try a unique prefix to avoid confusion, such as myplugin_ before the slug.

Questions & Answers

Yes. You can use popular plugins such as “Custom Post Type UI” or “Toolset.” However, coding it yourself gives you more control and a better understanding of what’s happening.

The best practice is to register CPTs in a plugin, not in the theme. That way, if you switch themes, your content structure remains intact.

If you set supports => [‘custom-fields’], WordPress enables the basic custom fields meta box. For more advanced control, use custom meta boxes or the register_post_meta() function to define structured metadata, possibly integrated with the REST API.

Yes, you can use register_post_type() or register_post_status() to adjust the capabilities or features of existing post types, but proceed carefully. It’s usually simpler and safer to create your own CPT than drastically altering core ones.

Use the __() function and a textdomain. Ensure your plugin or theme is set up for localization. Create language files for translations. For example, __() and _e() functions mark strings for translation.

Yes, you can use the menu_position argument. Common positions are 5 for posts, 20 for pages, and so on. Adjusting menu_position helps organize the admin interface according to your preference.

Additional Tips and Best Practices

  1. Choose Descriptive Names and Slugs:
    Avoid overly generic names like “content” or “item.” Use descriptive names that communicate the CPT’s purpose. For example, movie or portfolio_item is clearer than data_entry.
  2. Set has_archive to True if Needed:
    If you want an archive page listing all items of your CPT, set has_archive => true. You can then view all items at your-site.com/custom/ (if your rewrite slug is custom).
  3. Consider Capability Control:
    If your site has multiple user roles, define custom capabilities to control who can edit or publish items in your CPT. It is useful in multi-author or membership sites.
  4. REST API Enhancements:
    If you rely heavily on the REST API, consider registering custom endpoints or controllers for advanced functionalities, like filtering CPT results or returning additional metadata. The rest_controller_class argument lets you specify a custom controller.
  5. Testing and Debugging:
    Always test your CPT on a staging site first. Check the front end (archive, single pages), the REST API endpoints, and the admin interface. Use the Query Monitor plugin to see if queries run as expected and ensure no performance issues arise.

Conclusion

The ability to create a new post type in WordPress means there is no set way to how your content can be organized and displayed. By taking the right steps—such as registering it on the init hook, setting rewrite rules, enabling show_in_rest, and choosing the right UI settings—you seamlessly integrate your CPT into the WordPress ecosystem. This results in a fully customized, user-friendly content type that is easy to work with pretty permalinks, REST API, and the block editor.

Your new CPT can now serve your site’s unique content needs, whether you’re building a portfolio, managing events, listing products, or publishing a specialized type of content. By following best practices—like placing CPT code in a plugin, testing thoroughly, and avoiding unnecessary rewrite rule flushes—you ensure a stable, maintainable environment.

God willing, this guide helps you confidently create and integrate custom post types, making WordPress an even more powerful and flexible platform for your projects.

About the writer

Hassan Tahir Author

Hassan Tahir wrote this article, drawing on his experience to clarify WordPress concepts and enhance developers’ understanding. Through his work, he aims to help both beginners and professionals refine their skills and tackle WordPress projects with greater confidence.

Leave a Reply

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

Lifetime Solutions:

VPS SSD

Lifetime Hosting

Lifetime Dedicated Servers