What is a WordPress custom post type?
WordPress stores almost all content in one table, wp_posts, and the post_type column decides what each row is: a post, a page, a menu item, a reusable block. A custom post type (CPT) adds your own type — books, events, team members, case studies, properties — with its own admin menu, editor screen, URLs, archive page and REST endpoint.
CPTs are registered with register_post_type() on the init hook. Most of the work is in the arguments: around 35 labels that make the admin read naturally (“Add New Book”, “No books found in Trash”), visibility flags, show_in_rest for the block editor, supports for the editor features, rewrite rules for pretty URLs and capabilities for permissions. Getting one of them wrong leads to the classic problems — the block editor not showing, archive pages returning 404, or editors losing access.
This generator writes the complete, translation-ready code following the WordPress Coding Standards: every label wrapped in __() or _x() with your text domain, prefixed function names, and either a functions.php snippet or a standalone plugin that flushes rewrite rules on activation.
How to use the WordPress Custom Post Type Generator
- Enter the post type key (singular, lowercase, max 20 characters). Singular and plural names are suggested automatically — adjust them if the plural is irregular.
- Set your text domain and a unique function prefix, then choose snippet or plugin output.
- Tick the editor features under Supports, and keep
show_in_reston for the block editor. - Configure the menu icon, archive and rewrite slug. Hints show the resulting URLs.
- Copy or download the code, add it to a plugin or child theme, then flush permalinks by saving Settings → Permalinks.
Example
The core of the generated code for a “book” post type (labels shortened):
function myplugin_register_book_post_type() {
$labels = array(
'name' => _x( 'Books', 'post type general name', 'my-plugin' ),
'singular_name' => _x( 'Book', 'post type singular name', 'my-plugin' ),
// …33 more labels
);
register_post_type(
'book',
array(
'labels' => $labels,
'public' => true,
'show_in_rest' => true,
'has_archive' => true,
'rewrite' => array( 'slug' => 'books', 'with_front' => false ),
'supports' => array( 'title', 'editor', 'thumbnail', 'excerpt' ),
)
);
}
add_action( 'init', 'myplugin_register_book_post_type' );Plugin or theme?
Register post types in a plugin (or a must-use plugin), not in a theme. Content outlives designs: if a CPT lives in functions.php and the theme is switched, the posts are still in the database but disappear from the admin and their URLs return 404. The plugin output also flushes rewrite rules on activation, which is the only safe moment to call flush_rewrite_rules() — never call it on every init.
Next steps
- Group your entries with a custom taxonomy from the Taxonomy Generator, such as a “Genre” for books.
- Add structured fields like price, ISBN or event date with the ACF Field Generator.
- List entries anywhere with a shortcode, and read the custom post types guide for templates such as
single-book.phpandarchive-book.php.
Frequently Asked Questions
Why does my custom post type show a 404?
The rewrite rules have not been regenerated. Visit Settings → Permalinks and click Save Changes. Also check that no page uses the same slug as the post type’s rewrite or archive slug.
Why is the block editor not showing for my post type?
The block editor needs show_in_rest => true and editor in supports. Without REST, WordPress falls back to the Classic Editor.
What is the difference between public and publicly_queryable?
public is a shortcut that sets defaults for several flags (search, admin UI, nav menus). publicly_queryable alone controls whether single entries and archives can be viewed on the front end — set it to false for internal data like testimonials shown only via a block.
Why is the post type key limited to 20 characters?
The post_type column in wp_posts is varchar(20). register_post_type() refuses longer keys with a “doing it wrong” notice. Keys are permanent — renaming later means migrating the database.
Should I use capability_type “post” or custom capabilities?
Use post unless different roles need different access. Custom capabilities (with map_meta_cap) let you, for example, allow a “Shop Manager” to edit products but not posts — but you must add the new capabilities to roles yourself.
How do I show custom post types on the blog page?
Hook pre_get_posts, check $query->is_main_query() && $query->is_home(), and set post_type to an array such as array( 'post', 'book' ). More examples are in functions.php Snippets.