Would you like to extend WordPress functionalities beyond posts and pages?
Custom Post Types (CPTs) are a powerful tool that allows you to transform your website into any kind of custom platform or application — from directories to portfolios, catalogs, courses, service packages, and more.
In this complete guide, you’ll learn:
What CPTs are and why they matter
When and why to use them
How to create them (with plugins or manually with code)
Common errors and how to fix them
Tips and best practices
Practical examples
What Are Custom Post Types (CPT) in WordPress?
A Custom Post Type (CPT) is a custom content type that you can register in WordPress to display information that doesn’t fit the traditional post or page format.
For example:
A real estate site could use a CPT called “Properties.”
A restaurant might create a CPT called “Menu.”
A travel agency could use a CPT named “Packages.”
By default, WordPress includes content types such as:
post
(blog posts)page
(pages)attachment
(media files)
CPTs let you expand that functionality without altering WordPress core.
When Should You Use a Custom Post Type?
Use a CPT when:
You need a custom content type with its own admin interface
You want to organize content that is not a post or page
You require custom fields, taxonomies, or specific features
Benefits:
Clear and scalable content structure
Logical separation of data
Better user experience in the admin panel
Improved SEO with clean, structured URLs
Drawbacks (if not used properly):
Structural complexity if overused
Requires technical knowledge if done with code
Possible conflicts with poorly developed themes or plugins
How to Create a CPT in WordPress
You have two main options:
1. Use a Plugin (Easy and Quick)
Plugins like:
Custom Post Type UI
Pods
JetEngine (for Elementor)
Let you create CPTs without writing code. Simply:
Install the plugin
Access its interface
Create the new post type
Define labels, capabilities, and features
2. Use PHP Code (Full Control and Professional Approach)
If you prefer full control over your CPT behavior or you’re developing a custom theme/plugin, you can register your Custom Post Type manually in the functions.php
file or in a custom plugin.
Below is a complete example of how to register a CPT called “Package”:
if ( ! function_exists('custom_post_type_Package') ) {
// Register Custom Post Type
function custom_post_type_Package() {
$labels = array(
'name' => _x( 'Packages', 'Post Type General Name', 'text_domain' ),
'singular_name' => _x( 'Package', 'Post Type Singular Name', 'text_domain' ),
'menu_name' => __( 'Packages', 'text_domain' ),
'name_admin_bar' => __( 'Packages', 'text_domain' ),
'archives' => __( 'Archives', 'text_domain' ),
'attributes' => __( 'Atributes', 'text_domain' ),
'parent_item_colon' => __( 'Parent:', 'text_domain' ),
'all_items' => __( 'All Packages', 'text_domain' ),
'add_new_item' => __( 'Add new Package', 'text_domain' ),
'add_new' => __( 'Add new', 'text_domain' ),
'new_item' => __( 'New Package', 'text_domain' ),
'edit_item' => __( 'Edit Package', 'text_domain' ),
'update_item' => __( 'Update Package', 'text_domain' ),
'view_item' => __( 'View Package', 'text_domain' ),
'view_items' => __( 'View Packages', 'text_domain' ),
'search_items' => __( 'Search Package', 'text_domain' ),
'not_found' => __( 'Not found', 'text_domain' ),
'not_found_in_trash' => __( 'Not found in trash', 'text_domain' ),
'featured_image' => __( 'Featured image', 'text_domain' ),
'set_featured_image' => __( 'Select featured image', 'text_domain' ),
'remove_featured_image' => __( 'Delete featured image', 'text_domain' ),
'use_featured_image' => __( 'Use as featured image', 'text_domain' ),
'insert_into_item' => __( 'Insert', 'text_domain' ),
'uploaded_to_this_item' => __( 'Upload', 'text_domain' ),
'items_list' => __( 'List', 'text_domain' ),
'items_list_navigation' => __( 'Navigation list', 'text_domain' ),
'filter_items_list' => __( 'Filterr', 'text_domain' ),
);
$args = array(
'label' => __( 'Package', 'text_domain' ),
'description' => __( 'CPT Packages', 'text_domain' ),
'labels' => $labels,
'supports' => array( 'title', 'editor', 'author', 'excerpt', 'thumbnail', 'revisions', ),
/*'taxonomies' => array( 'category', 'post_tag' ),*/
'menu_icon' => 'dashicons-admin-home',
'hierarchical' => false,
'public' => true,
'show_ui' => true,
'show_in_menu' => true,
'menu_position' => 5,
'show_in_admin_bar' => true,
'show_in_nav_menus' => true,
'can_export' => true,
'has_archive' => true,
'exclude_from_search' => false,
'publicly_queryable' => true,
'capability_type' => 'page',
'show_in_rest' => true,
);
register_post_type( 'Package', $args );
register_taxonomy(
"package-category",
array("package"),
array(
"hierarchical" => true,
"label" => "Categories",
"singular_label" => "Category",
"rewrite" => array(
'slug' => 'package-category',
'with_front'=> false
)
)
);
}
add_action( 'init', 'custom_post_type_Package', 0 );
}
Line-by-Line Code Explanation:
if ( ! function_exists('custom_post_type_Package') )
: Ensures the function isn’t declared more than once.function custom_post_type_Package() { … }
: Declares the function that registers the CPT.$labels
: Defines all labels shown in the WordPress admin for this CPT (name, buttons, menu text, etc.)$args
: Contains the CPT configuration:'label'
and'description'
: General name and description'supports'
: Features like editor, featured image, author, etc.'menu_icon'
: Icon displayed in the admin menu (a house in this case)'public'
,'show_ui'
,'has_archive'
: Control CPT visibility in the site and admin'show_in_rest' => true
: Enables support for Gutenberg editor and REST API
register_post_type( 'Package', $args )
: Registers the CPT with the defined argumentsregister_taxonomy(...)
: Registers a custom taxonomypackage-category
associated with the CPT
Note: Even though
register_post_type
uses'Package'
(uppercase), it’s best practice to use lowercase ('package'
) in production to avoid URL conflicts. Also, visit Settings > Permalinks > Save Changes after registering a new CPT to avoid 404 errors.
Common Errors When Creating CPTs and How to Fix Them
Error | Solution |
---|---|
CPT doesn’t appear in admin | Make sure show_ui and show_in_menu are set to true |
Doesn’t show on front-end | Check that public and has_archive are enabled |
Doesn’t appear in block editor | Enable show_in_rest |
URL issues / 404 errors | Re-save permalinks in Settings > Permalinks |
Best Practices When Using CPTs
Use lowercase, no spaces:
portfolio
,service
,project
Register custom taxonomies to categorize your CPTs
Use
show_in_rest
to enable Gutenberg and REST APIIf using code, place it in the child theme’s
functions.php
or ideally in a custom pluginFlush rewrite rules once when registering new CPTs (
flush_rewrite_rules()
only once)
Practical Example: Travel Agency Website
CPT: Packages
Taxonomy: Countries or Trip Types (adventure, cultural, luxury)
Custom Fields:
Price
Departure Date
Duration
Featured Image
Advantages:
Organized content management
Easy user navigation
Enhanced SEO through custom URLs like:
digitalsolutionsnetwork.com/package/adventure-machu-picchu
Custom Post Types in WordPress are essential tools for building advanced, organized, and scalable websites. Whether created via plugin or manually with code, learning to use them properly opens the door to professional web development.
Looking to customize your WordPress site or develop a tailored solution?
At Digital Solutions Network, we can help. Contact us and take the next step toward a fully optimized and functional website.