W
WordPress SOPKnowledge Base
Search
← All topics

WordPress Custom Post Types and Taxonomies

runnable

Register custom post types and taxonomies in functions.php with Gutenberg and REST API support.

developmentcustom-post-typestaxonomiesrest-api
Agent trigger phrases: custom post type wordpress · register_post_type · wordpress taxonomy · cpt wordpress · wordpress content types

Register custom post types (CPTs) and taxonomies in functions.php or a dedicated plugin file. Never in theme files if the content needs to persist across theme changes.

Register a Custom Post Type

function register_custom_post_types() {
    register_post_type('portfolio', [
        'labels' => [
            'name'               => __('Portfolio', 'my-theme'),
            'singular_name'      => __('Project', 'my-theme'),
            'add_new_item'       => __('Add New Project', 'my-theme'),
            'edit_item'          => __('Edit Project', 'my-theme'),
            'view_item'          => __('View Project', 'my-theme'),
            'search_items'       => __('Search Portfolio', 'my-theme'),
            'not_found'          => __('No projects found', 'my-theme'),
        ],
        'public'       => true,
        'has_archive'  => true,
        'menu_icon'    => 'dashicons-portfolio',
        'supports'     => ['title', 'editor', 'thumbnail', 'excerpt', 'custom-fields'],
        'rewrite'      => ['slug' => 'portfolio', 'with_front' => false],
        'show_in_rest' => true, // Required for Gutenberg support
        'menu_position' => 5,
    ]);
}
add_action('init', 'register_custom_post_types');

Register a Custom Taxonomy

function register_custom_taxonomies() {
    register_taxonomy('project_type', ['portfolio'], [
        'labels' => [
            'name'              => __('Project Types', 'my-theme'),
            'singular_name'     => __('Project Type', 'my-theme'),
            'search_items'      => __('Search Project Types', 'my-theme'),
            'all_items'         => __('All Project Types', 'my-theme'),
            'edit_item'         => __('Edit Project Type', 'my-theme'),
            'update_item'       => __('Update Project Type', 'my-theme'),
            'add_new_item'      => __('Add New Project Type', 'my-theme'),
        ],
        'hierarchical'  => true,  // true = category-like, false = tag-like
        'public'        => true,
        'show_in_rest'  => true,
        'rewrite'       => ['slug' => 'project-type'],
    ]);
}
add_action('init', 'register_custom_taxonomies');

Flush Rewrite Rules

After registering new CPTs, flush rewrite rules once:

// In activation hook of a plugin
register_activation_hook(__FILE__, function() {
    register_custom_post_types();
    flush_rewrite_rules();
});

Or navigate to Settings > Permalinks and click Save in WordPress admin.

Query CPT Posts

$projects = new WP_Query([
    'post_type'      => 'portfolio',
    'posts_per_page' => 12,
    'tax_query'      => [
        [
            'taxonomy' => 'project_type',
            'field'    => 'slug',
            'terms'    => 'web-design',
        ],
    ],
    'orderby' => 'date',
    'order'   => 'DESC',
]);

Common Dashicons for Menu Icons

dashicons-portfolio, dashicons-testimonial, dashicons-location-alt, dashicons-star-filled, dashicons-hammer, dashicons-admin-tools