Development 2 min read

WordPress Custom Post Types: The Right Way to Model Your Data

Custom Post Types are one of WordPress’s most powerful and most misused features. Here’s how to use them correctly — and the mistakes that will hurt you later.

Custom Post Types (CPTs) are where WordPress transitions from blogging platform to application framework. Used well, they let you model almost any data structure cleanly. Used carelessly, they create a maintenance nightmare.

What a CPT actually is

Everything in WordPress is a post. Pages are posts. Attachments are posts. A CPT is just a post with a custom post_type value. It gets its own admin menu, its own template files, and its own query arguments.

When to use a CPT

Use a CPT when you have a distinct content type with its own admin workflow, its own display template, and structured metadata beyond title and content. Good examples: Projects, Team Members, Testimonials, Events, Case Studies.

Bad examples: anything you could model with a category or tag, or anything where a simple custom field on an existing post type would do.

Register it properly

register_post_type( 'project', [
    'labels'        => [ 'name' => 'Projects', 'singular_name' => 'Project' ],
    'public'        => true,
    'has_archive'   => true,
    'show_in_rest'  => true,
    'supports'      => [ 'title', 'editor', 'thumbnail' ],
    'menu_icon'     => 'dashicons-portfolio',
    'rewrite'       => [ 'slug' => 'projects' ],
] );

Two things people often skip: show_in_rest => true (required for Gutenberg) and a sensible rewrite slug.

Metadata: post meta vs custom tables

WordPress stores CPT metadata in wp_postmeta as key-value pairs. This works well for small volumes. For CPTs with hundreds of thousands of rows or complex relational queries, consider a custom table. For most projects under 10,000 records, post meta with a well-named key prefix is completely fine.

The mistake that hurts later

The most common mistake: using a CPT when you should use a taxonomy, or vice versa. The rule is simple — if an item is a thing (a project, a person, an event), it’s a CPT. If it’s a label that groups things (a category, a status, a skill), it’s a taxonomy. Getting this right early saves significant refactoring time later.