With the installation of WordPress, we only get three built-in content types at the back end. They are posts, pages, and media. Nevertheless, WordPress in the present times has become quite flexible and fostered.
In the creation of custom post types in WordPress without using plugins, you need to manually make changes by adding the required code in your theme’s function.php file or in a site-specific plugin.
The term “post type” determines different types of content that can hold and present in WordPress. By default, there already are several basic post types in WordPress which are obtained with the installation process. They are:
- Post: It is a post type in WordPress which is most typically used by blogs. They are generally displayed in reverse sequential order by time in a blog.
- Page: Pages unlike posts are not displayed in a reverse time-based order and can be placed in a hierarchical order where a page can be a parent/child of another page. Moreover, pages do not make use of tags and categories like posts.
- Attachment: It is a special post type as they hold information about media that are being uploaded to your WordPress website. Attachments make use of the wp_postmeta table in order to store extra details such as metadata for images and videos that have been added to your website.
- Revision: It is considered as one of the most important post types as they are used to create a history of other post types in case you commit a mistake and would want to roll back to a previous version. You cannot technically edit revisions directly unless you restore a revision. They are editable in the same manner as posts and are stored in the wp_posts table just like any other post type.
- Navigation menu: Navigation menus in WordPress are a collection of links that can be used to navigate your website. This post type gives you permission to create custom lists of links to different locations on your website that is being used by the visitors and are edited in the theme section of the dashboard.
- Custom CSS: It is a theme-specific post type that is used to store CSS saved from The Customizers Additional CSS screen. Although each theme can have its own custom CSS post, the only active themes Custom CSS post is actually used.
- Changesets: Changesets being similar to revision is specifically used to keep the customizer in a persistent state. WordPress will try to keep up with content changes made through Customizer during the user session in a customize_changeset post and attempt to restore them while you exit your current session.

But sometimes you may want to create your version of the post and call them whatever you want. It could be a portfolio, Testimonials, Products, etc. For this, we need to create our own custom posts type without using plugins. According to WordPress Codex, “Custom Post Types” which is also known as “Custom Content Types” is the specific type of post types that can be added to your WordPress using a simple function called register_post_type(). This function allows you to add the new custom post type in WordPress in preference with a number of specifics such as supported features, accessibility, and labels.
Custom post types modify a WordPress site from a blogging platform into a cogent Content Management System (CMS). Essentially, they permit you to go beyond posts and pages by creating different content types for your website. The easiest and safest way for beginners to create a custom post type is by making the use of plugins available in WordPress.
Now, getting into the creation of custom post types without using plugins, it requires a programming knowledge as you need to manually make changes by adding the required code in your theme’s function.php file or in a site-specific plugin. You will not have to face a problem of losing a custom post type like in using a plugin as custom post types will disappear when the plugin is deactivated. Any data you have in those custom post types will still be there, but your custom post type will be unregistered and will not be accessible from the admin area.
Let us understand this with a fully working example. Let’s have a look at the following code.
// Our custom post type function
function create_posttype() {
register_post_type( ‘books’,
// CPT Options
array(
‘labels’ => array(
‘name’ => __( ‘Books’ ),
‘singular_name’ => __( ‘Books’ )
),
‘public’ => true,
‘has_archive’ => true,
‘rewrite’ => array(‘slug’ => ‘books’),
‘show_in_rest’ => true,
)
);
}
// Hooking up our function to theme setup
add_action( ‘init’, ‘create_posttype’ );
What this code does is that it registers a post type ‘books’ with an array of arguments. Included arguments are the options of our custom post type. The register_post_type() function takes the arguments.
This array is divided into two parts. The first part is labeled, which itself is an array. The second part contains other arguments like public visibility, has an archive, slug, and show_in_rest enables block editor support.
Now let’s have a look at an elaborative piece of code that adds more alternatives to your custom post type.
/*
* Creating a function to create our CPT
*/
function custom_post_type() {
// Set UI labels for Custom Post Type
$labels = array(
‘name’ => _x( ‘Books’, ‘Post Type General Name’, ‘thenotebook’ ),
‘singular_name’ => _x( ‘Book’, ‘Post Type Singular Name’, ‘thenotebook’ ),
‘menu_name’ => __( ‘Books’, ‘thenotebook’ ),
‘parent_item_colon’ => __( ‘Parent Book’, ‘thenotebook’ ),
‘all_items’ => __( ‘All Books’, ‘thenotebook’ ),
‘view_item’ => __( ‘View Book’, ‘thenotebook’ ),
‘add_new_item’ => __( ‘Add New Book’, ‘thenotebook’ ),
‘add_new’ => __( ‘Add New’, ‘thenotebook’ ),
‘edit_item’ => __( ‘Edit Book’, ‘thenotebook’ ),
‘update_item’ => __( ‘Update Book’, ‘thenotebook’ ),
‘search_items’ => __( ‘Search Book’, ‘thenotebook’ ),
‘not_found’ => __( ‘Not Found’, ‘thenotebook’ ),
‘not_found_in_trash’ => __( ‘Not found in Trash’, ‘thenotebook’ ),
);
// Set other options for Custom Post Type
$args = array(
‘label’ => __( ‘books’, ‘thenotebook’ ),
‘description’ => __( ‘Book news and reviews’, ‘thenotebook’ ),
‘labels’ => $labels,
// Features this CPT supports in Post Editor
‘supports’ => array( ‘title’, ‘editor’, ‘excerpt’, ‘author’, ‘thumbnail’, ‘comments’, ‘revisions’, ‘custom-fields’, ),
// You can associate this CPT with a taxonomy or custom taxonomy.
‘taxonomies’ => array( ‘genres’ ),
/* A hierarchical CPT is like Pages and can have
* Parent and child items. A non-hierarchical CPT
* is like Posts.
*/
‘hierarchical’ => false,
‘public’ => true,
‘show_ui’ => true,
‘show_in_menu’ => true,
‘show_in_nav_menus’ => true,
‘show_in_admin_bar’ => true,
‘menu_position’ => 5,
‘can_export’ => true,
‘has_archive’ => true,
‘exclude_from_search’ => false,
‘publicly_queryable’ => true,
‘capability_type’ => ‘post’,
‘show_in_rest’ => true,
);
// Registering your Custom Post Type
register_post_type( ‘books’, $args );
}
/* Hook into the ‘init’ action so that the function
* Containing our post type registration is not
* unnecessarily executed.
*/
add_action( ‘init’, ‘custom_post_type’, 0 );
Well, you can see, we have added many more choices to the custom post type with this code. It will add more features like support for revisions, highlighted image, custom fields, and many more.
We have also connected this custom post type with a custom taxonomy defined as genres. Taxonomies in WordPress are used as a way to group posts and custom post types together.
You may also lay your eye on the part where we have set the hierarchical value to be false. You can set this value to true if you would like your custom post type to behave like Pages.
The next thing to be noticed is the frequent usage of “thenotebook” string, this is called the text-domain. If your theme is ready for translation and wants to have your custom post types be translated, then you will need to mention the text domain used by your theme. You can find your theme’s text domain inside the style.css file in your theme directory. The text domain will be mentioned in the header of the file.
Getting deep into knowing what the terms used mean.
- $supports: It defines that the post type is compatible and assists all the required features.
- $labels: It specifies that the post type is referred decently to the admin area.
- $args: It is an array of arguments that specifies a permalink slug of the news, and a menu position located just beneath the Posts menu.
Array Arguments
- name: A general name for the post type, usually plural.
- singular_name: Name for one object of the post type.
- add_new: The add new text, like “Add New” for posts in the admin panel.
- add_new_item: The header text for the newly created entry in the admin panel, like, “Add New Post” for posts in the admin panel.
- edit_item: The text foe editing an item.
- new_item: The new item text.
- view_item: The view item text.
- search_items: The text for a search for this item.
- not_found: The text in case nothing was found.
- not_found_trash: The text if the item was not found in the trash.
- all_items: The all items text.
- public: Determines whether the post type is public or not.
It’s highly recommended that you define custom post types within a plugin or must-use plugin to make sure that if you switch themes, the post type does not get lost. That way you can best ensure your content is always accessible. Well, this is a way to create a Custom Post Type in WordPress without making the use of a plugin which is a pretty complex thing to do.