
💡 How to create a custom post type in WordPress: code and plugin
Standard Posts and Pages cover most tasks for a typical website. But when a product catalog, real estate database, or project portfolio comes into play, these two content types are no longer enough. Mixing products with blog drafts is inconvenient, and overloading pages with custom fields is a path to admin panel chaos.
WordPress solves this problem through Custom Post Types, Custom Post Types (CPT). You create a separate section in the sidebar menu with its own fields, categories, and display templates. Products live separately from articles. Events don't get mixed up with pages. Data stays where it belongs.
Below are two tested methods: through code using register_post_type() (flexible, zero extra plugins) and through the free plugin CPT UI (no PHP required). Both work on WordPress 6.7+ and have been tested on production sites.
💡 Quick overview:
- Register a new post type through the core function
register_post_type(), without plugins, with full control over parameters. - Code-free alternative: the Custom Post Type UI plugin provides a visual interface, lowers the barrier to entry, and eliminates syntax errors.
- After registration, the type appears in the sidebar menu. Final touches: taxonomies, fields, and permalink flush.
What is a custom post type
Any content in WordPress is a record with a post_type field in the wp_posts table. For blog posts, it's post. For pages, it's page. For attachments, it's attachment. A custom type adds another value: product for products, event for events, portfolio for projects. Whatever fits your site's needs.
Each CPT gets its own menu item in the admin panel, a URL structure (/events/webinar-2026/), its own taxonomies, and support for the editor blocks you need: thumbnails, excerpts, editor, comments. All of this is enabled through checkboxes in the supports array, no magic involved.
The main benefit is organization. Products don't float around in the blog feed. Events don't need to be filtered out from a general pile. Administration speeds up, and site search works more precisely.
Method 1: registration through code, register_post_type()
The basic method that has worked since WordPress 3.0. No third-party plugins needed. The code goes in your child theme's functions.php or, more safely, through the Code Snippets plugin: a code error won't crash the site but will simply disable that specific snippet.
Step 1: function on the init hook
The function hooks into init. Earlier won't work because the core isn't ready to accept registrations yet. Later means the type won't be picked up until the next request. Here's a minimal working template:
1 /** 2 * Registers a custom post type 'Books'. 3 * Place in functions.php of the child theme or via Code Snippets. 4 */ 5 function myprefix_register_book_post_type() { 6 register_post_type( 'book', array( 7 'labels' => array( 8 'name' => 'Books', 9 'singular_name' => 'Book', 10 'add_new_item' => 'Add New Book', 11 'edit_item' => 'Edit Book', 12 'view_item' => 'View Book', 13 'search_items' => 'Search Books', 14 'not_found' => 'No books found', 15 ), 16 'public' => true, 17 'has_archive' => true, 18 'rewrite' => array( 'slug' => 'books' ), 19 'supports' => array( 'title', 'editor', 'thumbnail', 'excerpt' ), 20 'show_in_rest' => true, 21 'menu_icon' => 'dashicons-book-alt', 22 ) ); 23 } 24 add_action( 'init', 'myprefix_register_book_post_type' );
After inserting the code, a "Books" section appears in the admin panel. Record URLs become site.ru/books/book-name/. It works immediately.
Step 2: key parameters
The function takes two arguments: a type key string (up to 20 characters, Latin letters and hyphens) and a settings array. Here are the parameters worth configuring deliberately:
Parameter | What it does | Recommendation |
|---|---|---|
| Visibility in admin and frontend |
|
| Archive page for all records of this type |
|
| URL structure (slug) | Set explicitly, without special characters |
| Editor blocks | Minimum: |
| Availability in the block editor |
|
| Icon in the sidebar menu | Choose from Dashicons |
Step 3: taxonomies
Post categories and tags are not attached to your CPT by default. Create your own:
1 function myprefix_register_book_taxonomies() { 2 register_taxonomy( 'genre', 'book', array( 3 'label' => 'Genres', 4 'rewrite' => array( 'slug' => 'genre' ), 5 'hierarchical' => true, 6 'show_in_rest' => true, 7 ) ); 8 9 register_taxonomy( 'book_tag', 'book', array( 10 'label' => 'Book Tags', 11 'rewrite' => array( 'slug' => 'book-tag' ), 12 'hierarchical' => false, 13 'show_in_rest' => true, 14 ) ); 15 } 16 add_action( 'init', 'myprefix_register_book_taxonomies' );
Taxonomies are registered through register_taxonomy() with an explicit binding to the post_type as the second parameter. hierarchical => true provides a tree structure like categories. false gives a flat list like tags.
Step 4: flush permalinks
After registration, go to Settings → Permalinks and click "Save Changes," even if you haven't changed anything. WordPress will rebuild the routing rules. Without this step, URLs like /books/ will return 404.
When code is enough
This method works well for one or two custom types when the structure doesn't change often. Plus: zero extra plugins and full control over every parameter. Minus: for complex relationships between multiple CPTs and frontend submission forms, the code quickly grows.
Method 2: without code, Custom Post Type UI plugin
If you don't want to touch functions.php, install Custom Post Type UI. A free plugin from WebDevStudios: one million active installations, 4.6 rating on WordPress.org. Updated in May 2026, tested up to WP 7.0.

How to create a CPT through the interface
- Install the plugin: Plugins → Add New → "Custom Post Type UI" → Install → Activate.
- Go to CPT UI → Add/Edit Post Types.
- Fill in the fields: Post Type Slug (Latin letters, for example
event), Plural Label ("Events"), Singular Label ("Event"). - In the Settings block, check: Public (True), Has Archive (True), Show in REST API (True).
- In the Supports block, check at minimum: Title, Editor, Thumbnail, Excerpt.
- Click Add Post Type.
Taxonomies are created in the same place, on the Add/Edit Taxonomies tab. Specify the binding to your post type, and categories will appear in the sidebar menu.
Strengths
Quick registration without risk of syntax errors: the plugin assembles a correct $args array on its own. The Tools tab enables type migration between sites through code export and import. CPT UI Pro adds blocks for frontend output and drag-and-drop columns in the admin panel, but the free version fully covers the registration task.
Note that the plugin only registers types and taxonomies. For frontend display, you'll need either theme templates, CPT UI Pro, or a combination with Advanced Custom Fields or Elementor.
Comparison: code versus plugin
Criterion | Code ( | Plugin (CPT UI) |
|---|---|---|
Barrier to entry | Requires understanding of PHP arrays | Interface similar to standard WP settings |
Number of CPTs | Unlimited | Unlimited |
Transfer between sites | Copy the code | Export/import through Tools |
Extra code on site | None | Plus one plugin |
Plugin dependency | None | Yes, CPT UI must be active |
Documentation |
In practice, a combination is often used: register types through CPT UI for speed, and add fields through Advanced Custom Fields. This gives you a visual configuration interface without a single line of PHP.
⁉️🤔 Frequently asked questions
Do I need to flush permalinks after creating a CPT?
Yes, always. Go to Settings → Permalinks and click "Save." WordPress will rebuild the routing rules. Without this, new URLs like
/books/will return 404.
How does CPT UI differ from ACF in type registration?
CPT UI only registers post types and taxonomies. ACF, starting from version 6.1, can also register CPTs, but its main strength is custom fields. The CPT UI + ACF combination gives you an interface for types and flexible fields. You're not locked into a single vendor.
Can I rename an existing post type?
There's no direct mechanism. When you change the
post_typein the database, old records disappear from the admin panel. The proper approach: create a new type with the desired name, migrate records with an SQL queryUPDATE wp_posts SET post_type = 'new_type' WHERE post_type = 'old_type', and only then remove the old registration. A full database backup before migration is mandatory.
Will custom types disappear when changing themes?
If types are registered in the theme's
functions.php, yes, when you change themes they will disappear from the admin panel. The records will remain in the database but won't be visible. Register CPTs in a separate plugin or through Code Snippets, and the types will survive any theme change.
How do I display CPT on the homepage in the general feed?
Add a filter in Code Snippets:
add_filter( 'pre_get_posts', function( $query ) { if ( $query->is_home() && $query->is_main_query() ) { $query->set( 'post_type', array( 'post', 'book' ) ); } } );. Substitute yourpost_typein the array, and the records will appear in the feed alongside regular posts.
Which method to choose: final summary
Both methods lead to the same result: a new section appears in the sidebar menu, ready to be filled with content. The difference is only in the tool.
If you work with the site hands-on and aren't afraid of functions.php, use code. One register_post_type() function, zero extra plugins. Suitable for landing pages, small catalogs, and projects with developer support.
If you're handing the site over to a client or building a content project with a dozen types, install CPT UI. The visual interface lowers the barrier to entry, and configuration export saves hours when migrating between platforms.
In any case, after registration, set up the display: single and archive templates in the theme or visual layout through CPT UI Pro / Elementor Pro. Without this step, the content will remain only in the admin panel.



