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
-
Naming Convention: Use unique, descriptive names for your post type
-
Flush Rewrite Rules: After adding a new CPT, visit Settings > Permalinks to refresh rewrite rules
-
Plugin Territory: Consider creating CPTs in a plugin rather than theme for portability
-
REST API: Enable
show_in_rest
for Gutenberg compatibility
-
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:
-
Custom Post Type UI (CPT UI)
-
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.