How to Create Custom Post Type in WordPress Without Plugin 2021

Share your love

In this tutorial, we will discuss how to create a WordPress plugin that can be used to add two new post types. After that, together, we will create new layouts for both post types using templates. Read the tutorial on creating a WordPress plugin for more information.

We will also activate the Post editor Custom Fields feature for each post type and display the new template fields.

With the Custom Post Types, you can control and manage the displayed content. If you are currently writing blog posts for personal consumption or telling stories about your daily stories, you can make them more interesting by creating layouts. Even if you blog about movie or music reviews, you can add a content area as a container for posts that don’t appear on the blog.

Understanding Custom Post Types

In short, WordPress custom post types allow you to sort your posts by content. The default post types in WordPress, for example, Post, Page, and Attachment.



Generally, you write all posts in the Admin Control Panel post area, then create a category. Each post of each type appears in the same list, so it will be difficult for you to distinguish between them based on the content type.

The custom post type has its link in the admin control panel that points to a list of posts of that type. As with normal posts, you can add categories to posts created in this way so that you are free to sort and display the posts as you wish.

In the example above, if a user accesses the movie database on the WordPress site, he cannot see the review article. It’s different if you create, for example, the ‘Action’ and ‘Romance’ categories. When the user opens the Action movies category page, he can see all the movie reviews and entries.



When creating new custom post types, you have various options. For example, specifying an appropriate place in the admin menu to display the link, determining whether the post type can appear in search results, determining whether the new post type supports excerpts or not, defines whether visitors may leave comments, and much more.

You can also change the various text (specified by the $ labels array ), for example, changing  Add New Post to  Add New Movie. As another example, you want to change the Featured Image text to  Add Poster.

You can also activate the custom field feature in the post editor. This feature was previously ‘hidden’ by default and must be enabled using the Screen Options link at the editor’s top.

Still, with the Movies and Movie Reviews examples, you can complete the Movie post with custom fields for several things, such as the year it was published, the director, IMDB rating, and so on. You can also create a film synopsis as post content that is posted on your site.

Usually, any custom fields created can be selected in any post type. For this reason, plugins are needed to limit the Appearance of each field.

Also Read: How To Change WordPress Theme (3 Ways)

How to Create Custom Post Types in WordPress

Since you will be making some significant changes, we recommend creating a WordPress plugin. You can also create custom post types in the theme’s functions.php file. This tutorial will create a WordPress plugin, and the example we use is a database/movie review.



To create custom post types, you have to write a new function that ‘calls’ the WordPress function  register_post_type ()  with two parameters. Your function must be hooked to the init action hook. If not, your custom post type cannot be registered correctly.

// The custom function MUST be hooked to the init action hook
add_action ( 'init' , 'lc_register_movie_post_type' ) ;  
// A custom function that calls register_post_type
function lc_register_movie_post_type ( ) { 
  // Set various pieces of text, $ labels is used inside the $ args array
 $ labels = array (
 'name' => _x ( 'Movies' , 'post type general name' ) ,  
 'singular_name' => _x ( 'Movie' , 'post type singular name' ) ,  
 ) ;
  // Set various pieces of information about the post type
 $ args = array (
 'labels' => $ labels,
 'description' => 'My custom post type' ,
 'public' => true,
 ) ;
  // Register the movie post type with all the information contained in the $ arguments array
 register_post_type ( 'movie' , $ args ) ; 

All custom functions must be prefixed to prevent problems with plugins or other theme functions. In this tutorial, we will use the initials LC.

Here are two parameters for  register_post_type () :

  1. Post type name, maximum of 20 characters, and must not contain spaces or capital letters.
  2. The $ args associative array containing information about the post type in the pair  ‘key’ => ‘value.’

Since arguments and labels are both arrays, what you have to create first is to write the label in the variable itself, in the $ args variable then ‘call’ the function in question.

$ Args array

Here are the most commonly used keys for the $ args array and they are all selectable (optional):

  • Labels –  Array specifying various kinds of text, such as ‘Add New Post’ can be changed to ‘Add New Movie.’ A description of the keys for this array label can be found under this list of keys.
  • Description – A short, descriptive summary of the post type. This key can be displayed in the post-type template but cannot be used anywhere.
  • Public – Although this post type is visible to authors and visitors alike, the default value is FALSE, which means this key does not appear in the Admin Control Panel.
  • exclude_from_search  Determines whether posts of this type appear in normal search results or not. The default value is the opposite of the public value.
  • publicly_queryable  – Determines if posts of this type are accessible by URL, such as, or in advanced use via the query_posts () function. The default value is the same as the public value.
  • show_ui  – Specifies whether the menu and post editor links appear in the Admin Control Panel or not. The default value is the same as the public value.
  • show_in_nav_menus  – Determines if posts of this type can be added to the navigation menu created via Appearance -> Menus. The default value is the same as the public value.
  • show_in_menu  – Determines if the post type link appears in the Admin Control Panel navigation menu. Note, FALSE hides the link, while TRUE adds a link as a new top-level link. By including a link, you can place the link as a sub-link of an existing top-level link, for example, entering  options-general.php  and putting it under the Settings link.
  • show_in_admin_bar  – Specifies whether the post type appears above the Admin bar, under the + New link or not.
  • menu_position – A new link in the Admin Control Panel navigation menu, 5 under Posts and 100 under Settings. To see the complete list of positions, go to the WordPress Codex entry.
  • Hierarchical – Determines if the post can be redirected to the Parent post. If TRUE, then the $ supports array must contain the ‘page-attributes’ feature.
  • Supports – Enables various post features that have been selected, such as feature images, excerpts, custom fields, and others. If this key is set to FALSE instead of an array, the editor for this post type will be disabled – this option is very useful if you want to lock all posts of this type but still view them (below is a list of array values).
  • Taxonomies – Array taxonomies that can be applied to posts of this type. Remember, register the taxonomy first, not create one!
  • has_archive  – Determines if the post type has an archive page, the URL follows the permalink structure, and the slug is the name you put in parameter 1 of register_post_types (), for example,]/ returns all moview_review posts.
  • query_var  – TRUE or FALSE sets posts to be viewed by typing the post type and post name as a query in the URL, for example, ‘ Movie = the matrix. If you entered a text string, then you can set the text to be used after the? Character. For example, if you enter the string ‘movie,’ the result will look like this: ‘? film = the matrix.

You can see the complete list on the WordPress Codex page for register_post_type ().

Array labels

The first key in the $ args array is named labels and must be set to an array. This array will determine various kinds of text related to the post type. Create a variable called $ labels to store all the information contained by the array labels.

Here are some important keys for the array labels, and all of them can be selected (optional):

  • Name – For post type, the name must be plural and generic, for example, movies.
  • singular_name  – The name for a post type, for example, movie.
  • add_new  – Change ‘Add New’, for example to ‘Add Movie’.
  • add_new_item  – Change ‘Add New Post’, for example to ‘Add New Movie’.
  • edit_item  – Change ‘Edit Post’, for example to ‘Edit Movie’.
  • featured_image  – Changes the ‘Featured Image’ in the post editor, such as ‘Movie Poster.’
  • set_featured_image  – Change the ‘Set Featured Image’, for example to ‘Add Movie Poster’.
  • menu_name – Replaces the text in top-level links. The default text for this link is the name of the key.

You can see the complete list on the WordPress Codex page for register_post_type ().

Array ‘supports’

// Enable specific features in the post editor for my post type
$ supports = array ('title', 'editor', 'author', 'thumbnail');

// Disable ALL features of the post editor for my post type
$ supports = FALSE;

One of the keys in the $ args array is named supports. With this simple array, you can write a list of post editor features that you want to enable for the custom post type. By default, only the title and editor are enabled.

If you don’t want it set to an array, you can write FALSE to turn off all post-editing features, including the title and content area. As a result, the post cannot be edited but can still be viewed.

Here is a list of post editor features that you can enable in the $ supports array :

  • title
  • editor
  • author – NOTE: this feature allows you to change the author post.
  • thumbnail
  • excerpt
  • trackbacks
  • custom-fields
  • comments
  • revisions
  • page-attributes
  • post-formats

How to Create New Custom Post Types with Plugins

After knowing what information is needed for this function, the next step is to create a WordPress plugin, write a custom function, and hook the function to the init action hook.

<? php
/ *
Plugin Name: My Custom Post Types
Description: Add post types for movies and movie reviews
Author: Liam Carberry
* /
// Hook <strong> lc_custom_post_movie () </strong> to the init action hook
add_action ( 'init' , 'lc_custom_post_movie' ) ;  
// The custom function to register a movie post type
function lc_custom_post_movie ( ) { 
  // Set the labels, this variable is used in the $ args array
 $ labels = array (
 'name' => __ ( 'Movies' ) ,  
 'singular_name' => __ ( 'Movie' ) ,  
 'add_new' => __ ( 'Add New Movie' ) ,  
 'add_new_item' => __ ( 'Add New Movie' ) ,  
 'edit_item' => __ ( 'Edit Movie' ) ,  
 'new_item' => __ ( 'New Movie' ) ,  
 'all_items' => __ ( 'All Movies' ) ,  
 'view_item' => __ ( 'View Movie' ) ,  
 'search_items' => __ ( 'Search Movies' ) ,  
 'featured_image' => 'Poster' ,
 'set_featured_image' => 'Add Poster'
 ) ;
  // The arguments for our post type, to be entered as parameter 2 of register_post_type ()
 $ args = array (
 'labels' => $ labels,
 'description' => 'Holds our movies and movie specific data' ,
 'public' => true,
 'menu_position' => 5 ,
 'supports' => array ( 'title' , 'editor' , 'thumbnail' , 'excerpt' , 'comments' , 'custom-fields' ) ,  
 'has_archive' => true,
 'show_in_admin_bar' => true,
 'show_in_nav_menus' => true,
 'has_archive' => true,
 'query_var' => 'movie'
 ) ;
  // Call the actual WordPress function
  // Parameter 1 is a name for the post type
  // Parameter 2 is the $ args array
 register_post_type ( 'movie' , $ args ) ; 
// Hook <strong> lc_custom_post_movie buaya () </strong> to the init action hook
add_action ( 'init' , 'lc_custom_post_movie]' ) ;  
// The custom function to register a movie review post type
function lc_custom_post_moviekamah ( ) { 
  // Set the labels, this variable is used in the $ args array
 $ labels = array (
 'name' => __ ( 'Movie Reviews' ) ,  
 'singular_name' => __ ( 'Movie Review' ) ,  
 'add_new' => __ ( 'Add New Movie Review' ) ,  
 'add_new_item' => __ ( 'Add New Movie Review' ) ,  
 'edit_item' => __ ( 'Edit Movie Review' ) ,  
 'new_item' => __ ( 'New Movie Review' ) ,  
 'all_items' => __ ( 'All Movie Reviews' ) ,  
 'view_item' => __ ( 'View Movie Reviews' ) ,  
 'search_items' => __ ( 'Search Movie Reviews' )  
 ) ;
  // The arguments for our post type, to be entered as parameter 2 of register_post_type ()
 $ args = array (
 'labels' => $ labels,
 'description' => 'Holds our movie reviews' ,
 'public' => true,
 'menu_position' => 6 ,
 'supports' => array ( 'title' , 'editor' , 'thumbnail' , 'excerpt' , 'comments' , 'custom-fields' ) ,  
 'has_archive' => true,
 'show_in_admin_bar' => true,
 'show_in_nav_menus' => true,
 'has_archive' => true
 ) ;
  // Call the actual WordPress function
  // Parameter 1 is a name for the post type
  // $ args array goes in parameter 2.
 register_post_type ( 'review' , $ args ) ; 

If this plugin is activated, you will see two new links in the Admin Control Panel navigation, directly under the Posts link.

Move the mouse around it to see the ‘View All’ and ‘Add New’ sub-links. Of course, the text in the sub-links will be the text you have created in the relevant $ labels array. See the editor to check where the label was changed.

Limiting Custom Fields to Specific Post Types

No need to wait long. Custom fields can be saved quickly after being added to a post. The custom fields you add will appear in the drop-down list for each post. Of course, this won’t be very clear if you want to find the required fields in a certain post type. If you’re going to restrict custom fields to appear only in certain post types, the easiest way is to use a plugin.

The Advanced Custom Fields plugin will add a simple editor to WordPress so you can create custom fields and set them to appear only in certain post types.

You can set the input type to the field, for example, a text box, email address text area, or something more sophisticated, like embed  Google maps so you can choose a location to highlight and display in the post.

Besides, you can also adjust the custom field layout on the edit screen.

Since these fields were created with plugins, to display them, you must use the plugin function. This will be explained in the next section.

New Post Type styling 

New post types need a template. For that, create a file in the main directory with the correct name. WordPress will use  single.php  and  archive.php to keep the posts and archives unchanged if the template is not created.

  • Templates for  individual posts  –  single- {post-type} .php
  • Template for archive pages –  archive- {post-type} .php

{post-type}  in filename must come from the name in parameter 1  register_post_type () .

  • For the post-movie type, the file names are  single-movie.php  and  archive-movie.php
  • For post-review movie types, the file names are  single-review.php and archive-review.php.

The best way to start styling the post type is to duplicate  single.php  or  archive.php  and rename it as mentioned above. This indicates that the entire structure is the same as the theme and that all the required template tags are in place.

Display Custom Fields in Post

To display custom fields created in the standard WordPress editor, you can use these two functions.

// Generates all custom fields attached to the post in a <ul> list
the_meta ();

// Get a specific piece of information
echo get_post_meta ($ post-> ID, 'Budget', TRUE);

This function redirects to the template file used to display your post.

the_meta ()

This function displays all the custom fields that are attached to the post in the <ul> list. The result will be like this:

<ul class = 'post-meta' >
 <span class = 'post-meta-key' > { your_key } </span> { your_value }

This function can be used anywhere in the single post template. However, if you want to put it somewhere else, then place it inside the WordPress loop.

get_post_meta ()

  • This function takes 3 parameters and returns the result.
  • The first parameter is the ID of the post. You can use the $ post-> ID  here to get the ID of the recently viewed post.
  • The second parameter is the name of the custom field and is very sensitive.
  • The third parameter is a  boolean named  $ single and can be set to TRUE (result as a string) or FALSE (result as an array).

NOTE: You can create multiple custom fields with the same name and different values. If there are multiple fields with the same name, set it to FALSE to return an array of all custom fields.

<? php
  $ movie_box_art = get_post_meta ($ post-> ID, 'Box Art', TRUE);

if (! empty ($ movie_box_art)) {?>
  <div class = "movie-poster-box">
    <img src = "<? php echo $ movie_poster?>" alt = "<? php single_post_title ();?>">
<? }?>

Since the get_post_meta ()  function can return a value, you can use the conditional statement’s value to change the layout accordingly.

In the example above, we checked whether the box art had been switched to a movie via the custom field. If  $ movie_box_art is filled, then echo the div and image.

Displays Advanced Custom Fields

// Display field value
the_field ('FIELD NAME');

// Return field value
get_field ('FIELD NAME');

The Advanced Custom Fields plugin has its functions and shortcodes for displaying fields.

the_field (‘FIELD NAME’);

This function displays the value of a particular field. We recommend that you use the Field Name that you entered when creating the field group.

get_field (‘FIELD NAME’);

This function returns the value of a specified field, very useful for conditional statements.

You will need this function. There are many advanced functions, and you can look for them in the official Documentation.


[acf field = "FIELD NAME"]

You can directly display the fields in the post using the shortcode above.

Display Custom Post Type on Front Page

/ Hook our custom function to the pre_get_posts action hook
add_action ('pre_get_posts', 'addkamah_to_frontpage');
// Alter the main query
function addlake_to_frontpage ($ query) {
    if (is_home () && $ query-> is_main_query ()) {
        $ query-> set ('post_type', array ('post', 'movie', 'review'));
    return $ query;

Custom post types are not displayed on the front page by default. Therefore, you have to write a new function to call the method set of the WordPress WP_Query object.

This function checks if the visitor is on the main page and checks whether the active query is the primary created by WordPress.

$ query-> set ()  takes two parameters:

  • The first parameter is the property you want to change if you want to change the  post_type property.
  • The second parameter is an array that you want as the value of the property  post_type.

In the code above, the array starts with ‘posts’ – this is because every post in WordPress has a post type ‘post,’ and we want to include it on the front page.

If you want only custom posts of a certain type on the front page, you can delete the ‘post’ and use the custom post type you want.

In the examples in this tutorial, we want to add ‘movie’ and ‘review’ to the array. As a result, you will see the regular posts on the front page, all the posts about the movie, and their reviews.

The value you enter must be the name used in parameter 1 of the register_post_type () function.


In this tutorial, you’ve learned how to create custom post types and know what required information. Apart from being flexible, custom post types also have several important features for websites built with WordPress.

If the custom post type is combined with categories and taxonomies, you can control the posts and content.

Share your love
Vishal Meena
Vishal Meena

Hey! I'm Vishal Meena from Rajasthan, India. A Digital Marketer and founder of WayToidea. I Share Strategies Related to Blogging, SEO and Digital Marketing.

Join 70+ Fellow Marketers & Bloggers

Get expert blogging & marketing tips every week straight to your inbox, and become a better marketer. Subscribe to the Blog below.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.