How to Create a Custom Post Type in WordPress

Marco B. Dec 25, 2024 Custom Post Types
How can I set up a new type of content on my website, like a portfolio or testimonials?
What is the process for registering a custom post type in WordPress using the register_post_type function?
Andy answered Dec 25, 2024

Creating Custom Post Types in WordPress

Basic Approach

Custom Post Types (CPTs) allow you to create specialized content types beyond regular posts and pages. They're perfect for portfolios, testimonials, products, or any structured content type.

Here's the basic code to register a custom post type, which should go in your theme's functions.php file or in a custom plugin:

Basic custom post type registration with essential settings:

function create_portfolio_post_type() {
    $args = array(
        'labels' => array(
            'name' => 'Portfolio',
            'singular_name' => 'Portfolio Item'
        ),
        'public' => true,
        'has_archive' => true,
        'supports' => array('title', 'editor', 'thumbnail'),
        'menu_icon' => 'dashicons-portfolio',
        'rewrite' => array('slug' => 'portfolio')
    );
    
    register_post_type('portfolio', $args);
}
add_action('init', 'create_portfolio_post_type');

Advanced Implementation

Extended version with more options and labels:

function create_advanced_portfolio_post_type() {
    $labels = array(
        'name' => 'Portfolio',
        'singular_name' => 'Portfolio Item',
        'add_new' => 'Add New',
        'add_new_item' => 'Add New Portfolio Item',
        'edit_item' => 'Edit Portfolio Item',
        'view_item' => 'View Portfolio Item',
        'search_items' => 'Search Portfolio',
        'not_found' => 'No portfolio items found',
        'not_found_in_trash' => 'No portfolio items found in trash'
    );
    
    $args = array(
        'labels' => $labels,
        'public' => true,
        'publicly_queryable' => true,
        'show_ui' => true,
        'show_in_menu' => true,
        'show_in_rest' => true, // Enable Gutenberg editor
        'query_var' => true,
        'rewrite' => array('slug' => 'portfolio'),
        'capability_type' => 'post',
        'has_archive' => true,
        'hierarchical' => false,
        'menu_position' => 5,
        'menu_icon' => 'dashicons-portfolio',
        'supports' => array('title', 'editor', 'thumbnail', 'excerpt', 'custom-fields')
    );
    
    register_post_type('portfolio', $args);
}
add_action('init', 'create_advanced_portfolio_post_type');

Best Practices

  1. Naming Convention: Use unique, descriptive names for your post type
  2. Flush Rewrite Rules: After adding a new CPT, visit Settings > Permalinks to refresh rewrite rules
  3. Plugin Territory: Consider creating CPTs in a plugin rather than theme for portability
  4. REST API: Enable show_in_rest for Gutenberg compatibility
  5. Security: Use appropriate capability_type and capabilities arguments

Common Pitfalls to Avoid

  • Don't use reserved post type names (post, page, attachment, revision, nav_menu_item)
  • Don't forget to flush rewrite rules after registration
  • Avoid registering CPTs on every page load
  • Don't use special characters or spaces in post type names

Plugin Solutions

If you prefer a no-code solution, these plugins can help:

  1. Custom Post Type UI (CPT UI)

  2. Pods

Template Hierarchy

Create these files in your theme to customize CPT display:

  • single-{post_type}.php - Single post display
  • archive-{post_type}.php - Archive page display

Security Considerations

Secure CPT registration code:

function register_secure_portfolio_post_type() {
    if (!current_user_can('manage_options')) {
        return;
    }
    
    $args = array(
        'capability_type' => 'post',
        'capabilities' => array(
            'create_posts' => 'create_posts',
            'edit_posts' => 'edit_posts',
            'edit_others_posts' => 'edit_others_posts',
            'publish_posts' => 'publish_posts',
            'read_private_posts' => 'read_private_posts'
        ),
        'map_meta_cap' => true,
        // ... other arguments as needed
    );
    
    register_post_type('portfolio', $args);
}
add_action('init', 'register_secure_portfolio_post_type');

After registration, always test your CPT thoroughly, checking both front-end display and admin functionality.