How to create custom post types in WordPress
You can create a custom post type manually with register_post_type() in a site-specific plugin, then add taxonomies, fields and templates as needed. For a visual site, a plugin such as JetEngine For Elementor is the faster route because it handles the structure without custom coding.
- 01Plan the content structure
- 02Register it in a plugin
- 03Add taxonomies and fields
- 04Create the display templates
- 05Refresh permalinks and test
- 06Use a visual builder instead
What you need
- WordPress administrator or code access
- A staging site or recent backup
- A clear name, URL slug and content fields for the new post type
- A child theme or site-specific plugin if you need custom templates
Plan the content structure
Decide what the content represents, such as books, case studies or team members. Choose a singular label, plural label, internal post type key and public URL slug. Keep the internal key lowercase, use no spaces, and avoid generic slugs that may conflict with pages, plugins or themes. WordPress limits post type keys to 20 characters.
List the editor features you need, such as title, editor, excerpt, featured image, author, revisions and custom fields. Decide whether the type needs a public single URL, an archive page, search results and the block editor.
Register it in a plugin
For the manual route, add the registration to a site-specific plugin rather than a theme. WordPress recommends a plugin so the content type remains available when you change themes. Register it on the init action with register_post_type().
A minimal example is function site_register_books(){register_post_type('site_book',array('labels'=>array('name'=>'Books','singular_name'=>'Book'),'public'=>true,'has_archive'=>true,'show_in_rest'=>true,'supports'=>array('title','editor','thumbnail'),'rewrite'=>array('slug'=>'books')));} add_action('init','site_register_books');. Replace the labels, key and slug with your own values, and prefix function names and identifiers to reduce collisions.
Add taxonomies and fields
Use a taxonomy when visitors need to group or filter entries, such as genres, locations or departments. Register a custom taxonomy with register_taxonomy() and associate it with your post type. Register the post type and taxonomy on init; custom taxonomies still need their own registration even when listed in the post type's taxonomies argument.
For a few simple values, enable custom fields or add a meta box. For repeaters, relationships, validation and user-friendly field screens, use a dedicated custom-fields or content-modelling plugin rather than storing unstructured text in the editor.
Create the display templates
Publish one test entry and decide how it should appear. A public post type normally needs a single-entry view and, if has_archive is enabled, an archive view. Your theme can provide these templates, or a block theme, page builder or dynamic-content plugin can render the title, content, featured image, taxonomy terms and custom fields.
Set show_in_rest to true if you want the post type in the block editor or REST API. Without it, the editor experience may be limited even though the post type appears in the admin area.
Refresh permalinks and test
Go to Settings → Permalinks and select Save Changes once after registering or changing the post type. This rebuilds WordPress rewrite rules. If you skip it, single entries and archives commonly return 404 errors even when the registration code is correct. WordPress documents flush_rewrite_rules() for controlled programmatic flushing, but it is an expensive operation and should not run on every request.
Test the admin menu, editor, single URL, archive URL, search, taxonomy URLs and navigation links. If unrelated pages also return 404 errors, check for a duplicate page or archive slug and for overly broad custom rewrite rules. Support threads repeatedly identify slug conflicts and stale rewrite rules as common causes.
Use a visual builder instead
The fast route is to use JetEngine For Elementor. It provides screens for custom post types, taxonomies, dynamic fields, listings, filters and Elementor layouts, so you do not need to maintain the registration code or build every listing template by hand.
Install and activate the plugin, create the post type and its fields, choose the public slug, then build a listing and single-item layout in Elementor. Still check the generated URLs, avoid slug conflicts, and save the WordPress permalink settings after changing rewrite-related options. This route is a good fit when the site needs several content types, relationships or visual templates; the manual route is fine for one simple type that will be maintained by a developer.
Let JetEngine For Elementor do it
Combines custom post types, fields, listings, and Elementor layouts for dynamic sites without custom coding.
Sources
- developer.wordpress.org /reference/functions/register_post_type/?utm_source=openai
- developer.wordpress.org /plugins/post-types/registering-custom-post-types/?utm_sourc…
- developer.wordpress.org /reference/functions/register_taxonomy/?utm_source=openai
- wordpress.org /support/topic/setting-custom-rewrite-slug-gives-404/?utm_so…
Questions
- Can I create a custom post type without a plugin?
- Yes. Add a small registration function to a site-specific plugin and call <code>register_post_type()</code> on <code>init</code>. You can then register taxonomies, add meta boxes and create theme templates yourself. The drawback is that you must maintain the code, permissions, fields, templates and rewrite behaviour when the site changes.
- Should a custom post type go in a theme or plugin?
- Put a custom post type in a plugin when the content should survive a theme change. If it lives in the theme, switching themes can remove the admin menu and make existing entries difficult to manage, even though the database records may remain. A site-specific plugin keeps the content model separate from presentation.
- Why does my custom post type URL show a 404 error?
- The usual first fix is to visit Settings → Permalinks and save the current structure to rebuild rewrite rules. If that does not work, check that the post type is registered on <code>init</code>, that the rewrite slug does not duplicate a page or another content type, and that no broad rewrite rule is intercepting the request.
- What does show_in_rest do for a custom post type?
- The <code>show_in_rest</code> argument exposes the post type through the WordPress REST API and is required for the block editor. Set it to <code>true</code> when you want to edit entries with Gutenberg or use REST-based integrations. It does not, by itself, create custom fields or front-end templates.
- Is a plugin better than code for custom post types?
- A plugin is usually better when you need several post types, custom fields, listings, filters or visual layouts. Code is perfectly suitable for one simple content type and gives you tighter control with fewer dependencies. JetEngine For Elementor is the faster option when the site owner needs to manage the structure and layouts without editing PHP.