Custom Post Types (CPT) in WordPress: What They Are and How to Create Them

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 arguments

  • register_taxonomy(...): Registers a custom taxonomy package-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

ErrorSolution
CPT doesn’t appear in adminMake sure show_ui and show_in_menu are set to true
Doesn’t show on front-endCheck that public and has_archive are enabled
Doesn’t appear in block editorEnable show_in_rest
URL issues / 404 errorsRe-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 API

  • If using code, place it in the child theme’s functions.php or ideally in a custom plugin

  • Flush 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.