How to Register a Custom Post Type (CPT) with register_post_type() in WordPress

September 23, 2025

Custom Post Types (CPTs) let you go beyond standard posts and pages in WordPress. They’re useful for organizing different kinds of content like portfolios, testimonials, products, or events. WordPress provides the register_post_type() function to create them. Here’s how you can register and configure a CPT the right way.


1. Basic Example

Add the following code to your theme’s functions.php file or a custom plugin:

<?php
function my_register_portfolio_cpt() {
    $args = array(
        'labels' => array(
            'name'          => __( 'Portfolios' ),
            'singular_name' => __( 'Portfolio' ),
        ),
        'public'       => true,
        'has_archive'  => true,
        'rewrite'      => array( 'slug' => 'portfolio' ),
        'supports'     => array( 'title', 'editor', 'thumbnail', 'excerpt' ),
        'show_in_rest' => true, // Enables Gutenberg and REST API
    );
    register_post_type( 'portfolio', $args );
}
add_action( 'init', 'my_register_portfolio_cpt' );
?>

This registers a new post type called Portfolio that supports the editor, featured images, and excerpts, and appears at yoursite.com/portfolio/.


2. Adding Labels for Better UI

To make your CPT more user-friendly, define a full set of labels:

<?php
function my_register_event_cpt() {
    $labels = array(
        'name'               => __( 'Events' ),
        'singular_name'      => __( 'Event' ),
        'add_new'            => __( 'Add New Event' ),
        'add_new_item'       => __( 'Add New Event' ),
        'edit_item'          => __( 'Edit Event' ),
        'new_item'           => __( 'New Event' ),
        'all_items'          => __( 'All Events' ),
        'view_item'          => __( 'View Event' ),
        'search_items'       => __( 'Search Events' ),
        'not_found'          => __( 'No events found' ),
        'not_found_in_trash' => __( 'No events found in Trash' ),
        'menu_name'          => __( 'Events' ),
    );

    $args = array(
        'labels'       => $labels,
        'public'       => true,
        'has_archive'  => true,
        'menu_icon'    => 'dashicons-calendar-alt',
        'rewrite'      => array( 'slug' => 'events' ),
        'supports'     => array( 'title', 'editor', 'thumbnail', 'custom-fields' ),
        'show_in_rest' => true,
    );

    register_post_type( 'event', $args );
}
add_action( 'init', 'my_register_event_cpt' );
?>

This creates an Events post type with its own menu icon and archive page.


3. Common register_post_type() Arguments

  • public – Controls visibility (true for front-end and admin).
  • has_archive – Enables an archive page (e.g., /events/).
  • rewrite – Defines the URL slug.
  • supports – Features available: title, editor, thumbnail, excerpt, custom-fields.
  • show_in_rest – Enables the block editor and REST API support.
  • menu_icon – Dashicon for the admin menu (e.g., dashicons-admin-users).
  • capability_type – Custom capabilities for advanced permissions.

4. Flushing Rewrite Rules

After registering a new CPT, you may need to refresh permalinks:

  1. Go to Settings → Permalinks.
  2. Click Save Changes (no need to modify anything).
  3. This flushes rewrite rules so your new CPT URLs work properly.

5. Summary

  1. Use register_post_type() inside an init hook to create CPTs.
  2. Define labels for better admin UI.
  3. Configure supports, rewrite, and show_in_rest as needed.
  4. Flush permalinks after adding a new post type.

With register_post_type(), you can organize your WordPress site into structured, custom content types tailored to your needs.

Avatar

Written by

satoshi

I’ve been building and customizing WordPress themes for over 10 years. In my free time, you’ll probably find me enjoying a good football match.