Custom Post Type Generator
Name it, pick what it supports, copy the register_post_type() call. Everything else stays out of your way until you ask for it.
// Generated with WP CodeKit — powered by GrowQuest (https://growquest.io). /** * Register the Books post type. */ function mytheme_register_book_post_type() { $labels = array( 'name' => __( 'Books', 'textdomain' ), 'singular_name' => __( 'Book', 'textdomain' ), 'menu_name' => __( 'Books', 'textdomain' ), 'add_new_item' => __( 'Add New Book', 'textdomain' ), 'new_item' => __( 'New Book', 'textdomain' ), 'edit_item' => __( 'Edit Book', 'textdomain' ), 'view_item' => __( 'View Book', 'textdomain' ), 'all_items' => __( 'All Books', 'textdomain' ), 'search_items' => __( 'Search Books', 'textdomain' ), 'not_found' => __( 'No books found.', 'textdomain' ), ); $args = array( 'labels' => $labels, 'public' => true, 'show_in_rest' => true, 'supports' => array( 'title', 'editor', 'thumbnail', 'excerpt' ), 'taxonomies' => array( 'category' ), 'has_archive' => true, ); register_post_type( 'book', $args ); } add_action( 'init', 'mytheme_register_book_post_type' );
About this generator
Post Type Generator Online
Describe the thing you are modelling — a Book, a Case Study, an Event — and this WordPress custom post type generator writes the whole register_post_type() call for you: all thirty-four labels derived from your singular and plural, the supports array, the taxonomies it is attached to, rewrite rules, REST arguments and capability mapping. Copy it as a bare snippet, a functions.php block, or a self-contained plugin file.
A Permalinks tab shows the single, archive and feed URLs your rewrite settings will actually produce, so you can see what a custom slug or with_front does before you flush anything. Free, no account, and nothing you type leaves the browser.
Reviewed Output tested on WordPress 6.8 · PHP 8.3
Why the register_post_type generator beats a copied CPT snippet
The snippet you find on a blog registers four labels and hopes for the best. Then an editor asks why the admin menu says "Posts", why the archive 404s, why the block editor will not load on the new type, or why two plugins fought over the key event. This generator writes the labels in full, checks the key against the rules WordPress actually enforces, and puts the rewrite flush in the one place it belongs.
Every label, not just four
Thirty-four labels are derived from your singular and plural — all_items, not_found_in_trash, item_scheduled, filter_by_date, the featured-image strings. Ten essentials are written out by default; flip one toggle to emit the complete set, or override any single label by hand.
The 20-character key limit, enforced
The post_type column is 20 characters wide. A longer key is flagged as an error with a one-click truncate, and anything outside lowercase letters, numbers, dashes and underscores gets a Sanitise key fix.
Reserved keys caught before you ship
post, page, attachment, revision, nav_menu_item, wp_block, wp_template, author, order, theme and the rest of the reserved list are rejected outright, and an unprefixed key gets a suggested project prefix you can apply in one click.
Block editor checks that matter
Ask for editor support with show_in_rest off and the generator warns that the type will silently fall back to the classic editor, with a Turn on REST fix. Mark the type hierarchical without page-attributes and it points out that the Parent selector will never appear.
A permalink preview before you flush
The Permalinks tab renders the single-post URL, the archive URL and the feed URL from your rewrite slug, with_front, has_archive and archive slug — the fastest way to spot a rewrite that will collide with a page of the same name.
Rewrite flushing in the right place
Plugin output adds a register_activation_hook() that registers the type and then calls flush_rewrite_rules() once. The functions.php output uses after_switch_theme instead. The bare snippet flushes nothing, because calling it on every init is the classic way to make a site slow.
How does the custom post type generator work?
Four steps. Name the thing, say how it behaves, tune the details you care about, then export.
- 01
Name it
Type the singular. The plural and the post type key are derived as you go — the key is slugified and capped at 20 characters — and you can override either. Add a description if the type needs one in the REST schema.
- 02
Set the behaviour and the content
Toggle public, hierarchical,
show_in_restandhas_archive, then pick thesupportsfeatures and the taxonomies to attach. Built-in categories and tags are one click; custom taxonomy slugs can be typed in. - 03
Open Labels and Advanced if you need them
Override any generated label, then set the admin menu position and Dashicon, REST base and namespace, permalink slug and
with_front,query_var, and the capability type —post,pageor a custom singular/plural pair withmap_meta_cap. - 04
Clear the checks, then export
Work through the Checks tab — most items have a one-click fix — choose Snippet,
functions.phpor Plugin file, then copy or download the.php.
Worked example — a public Books post type with archives and REST
Key book, singular Book, plural Books, public with an archive, block editor on, attached to the built-in category taxonomy. This is the Snippet output exactly as it copies.
/**
* Register the Books post type.
*/
function mytheme_register_book_post_type() {
$labels = array(
'name' => __( 'Books', 'textdomain' ),
'singular_name' => __( 'Book', 'textdomain' ),
'menu_name' => __( 'Books', 'textdomain' ),
'add_new_item' => __( 'Add New Book', 'textdomain' ),
'new_item' => __( 'New Book', 'textdomain' ),
'edit_item' => __( 'Edit Book', 'textdomain' ),
'view_item' => __( 'View Book', 'textdomain' ),
'all_items' => __( 'All Books', 'textdomain' ),
'search_items' => __( 'Search Books', 'textdomain' ),
'not_found' => __( 'No books found.', 'textdomain' ),
);
$args = array(
'labels' => $labels,
'public' => true,
'show_in_rest' => true,
'supports' => array( 'title', 'editor', 'thumbnail', 'excerpt' ),
'taxonomies' => array( 'category' ),
'has_archive' => true,
);
register_post_type( 'book', $args );
}
add_action( 'init', 'mytheme_register_book_post_type' );Arguments left at their WordPress defaults are omitted, which is why there is no hierarchical => false or query_var => true line here. Switch to Plugin file and the same code arrives with a header block and an activation hook that flushes rewrite rules once.
Custom post types — frequently asked questions
The questions that come up every time a new post type goes into a real site.
Why does my custom post type show a 404 on single posts?
Almost always stale rewrite rules. WordPress caches the rule set in the database, so a newly registered post type has no matching rule until the rules are rebuilt. Visit Settings > Permalinks once, or let the generated activation hook call flush_rewrite_rules() for you. Never call it on init — it rewrites an option on every request.
How long can a custom post type name be?
Twenty characters. The post_type column in the posts table is 20 characters wide, so a longer key registers without complaint but breaks the moment WordPress tries to save a post with it. The generator raises this as an error and offers a one-click truncate.
Should I register a post type in my theme or in a plugin?
In a plugin, in almost every case. Content belongs to the site, not to the design: put register_post_type() in a theme and switching themes makes every post of that type vanish from the admin. Use the Plugin file output, or a site-specific functionality plugin. The functions.php output exists for the cases where the type genuinely is part of the theme.
Why does my custom post type open the classic editor instead of blocks?
The block editor is a REST client. Without show_in_rest => true there is no REST route for the type, so WordPress falls back to the classic editor. Turn REST on and make sure editor is in the supports array; the generator warns when you have one without the other.
How do I add categories and tags to a custom post type?
Pass them in the taxonomies argument — the generator's Taxonomies chips write 'taxonomies' => array( 'category', 'post_tag' ) for you. That registers the relationship at the same time as the post type. For a taxonomy declared elsewhere, register_taxonomy_for_object_type() does the same job after the fact.
Can I change the post type key after posts exist?
Not safely. The key is stored in the post_type column of every row, so renaming it in code orphans all existing content — the posts stay in the database but nothing queries them. If you must rename, run a database update on post_type as part of the change, and flush rewrite rules afterwards.
Where do I paste the code from the Post Type generator?
You have three good options and one bad one. Best is a small site-specific plugin — the generator can output one for you, and it keeps working when you change theme. A code-snippets plugin is fine too. A child theme's functions.php works but ties the code to that theme. Never edit the parent theme: the next update overwrites it and your code is gone.
Why isn't my new post type showing up, or why do I get a 404?
Almost always stale rewrite rules. WordPress caches the URL routing table, so anything that adds URLs is invisible until that cache is rebuilt. Go to Settings → Permalinks and press Save Changes — you do not need to alter anything, just saving the page flushes the rules. Never call flush_rewrite_rules() on every page load; it is an expensive write and belongs in an activation hook.
Can I use the generated code in client work or a commercial plugin?
Yes. The output is ordinary WordPress API calls that you configured — there is no licence attached to it and no attribution required. Use it in client sites, commercial plugins and themes, or products you sell. The generated file carries a short credit comment you are free to delete.
The other Content generators in the library, plus the WordPress tools most often used alongside this one.