How to Create WordPress Custom Post Type

How to Create WordPress Custom Post Type

Custom post types make things a lot easy for non-technical people to use the WordPress admin to enter content. However the designer/developer still needs to have a reasonable grasp of PHP and needs to be prepared to get their hands dirty (unless they opt for a plugin that does the job for you like).

For our tutorial we’re going to be editing functions.php which is your theme directory. We’re simply adding to this file, so feel free to start at the top or at the bottom – just don’t change any of the code that’s already there.

With a few edits we are going to create a custom post type for our Webinars, and a template which will use this information. We’ll end up with a new panel in the admin menu which looks like this:

How to Create WordPress Custom Post Type

A new overview page called “Webinars”:

How to Create WordPress Custom Post Type

And a new place to enter our content:

How to Create WordPress Custom Post Type


Step 1

Here’s the first bit of code we need to add to functions.php which we’ll review below. The file functions.php is the most important file of your theme because it contains all functions and working methods/hooks. It’s right in the folder of your theme.

add_action('init', 'webinar_register');
function webinar_register()
    $labels = array(
        'name'               => _x('Webinars', 'post type general name'),
        'singular_name'      => _x('Webinar', 'post type singular name'),
        'add_new'            => _x('Add New', 'webinar item'),
        'add_new_item'       => __('Add New Webinar'),
        'edit_item'          => __('Edit Webinar'),
        'new_item'           => __('New Webinar'),
        'view_item'          => __('View Webinar'),
        'search_items'       => __('Search Webinar'),
        'not_found'          =>  __('Nothing found'),
        'not_found_in_trash' => __('Nothing found in Trash'),
        'parent_item_colon'  => '',
    $args = array(
        'labels'             => $labels,
        'public'             => true,
        'publicly_queryable' => true,
        'show_ui'            => true,
        'query_var'          => true,
        'rewrite'            => true,
        'capability_type'    => 'page',
        'hierarchical'       => false,
        'has_archive'        => 'webinars',
        'menu_position'      => 4,
        'menu_icon'          => 'dashicons-welcome-view-site',
        'supports'           => array( 'title',
         'taxonomies'        => array( 'webinar_themes', 'post_tag' ),
    register_post_type( 'webinar' , $args );


Anyone who’s worked with WordPress before will recognise the structure here. We’re adding an action when the WP Admin initialises to call the function webinar_register(). In that function we create two arrays, $labels and $args, and then use register_post_type() to pull it all together. In doing so we name the new custom post type ‘webinar’ and tell it to use the arguments from $args.

The devil is in the detail, so let’s run over some of those arguments. A full list can be found at the WordPress Codex documentation about  register_post_type(). First let’s look at $labels:

  • name this is the name for our new post type
  • singular_name how you’d refer to this in the singular (such as ‘Add new ****’)

You can probably work out the rest of $labels for yourself, as they simply refer to different circumstances in which the name of your custom post type would be used.

And now $args:

  • public should they be shown in the admin UI
  • show_ui should we display an admin panel for this custom post type
  • menu_icon a custom icon for the admin panel
  • capability_type WordPress will treat this as a ‘post’ or a ‘page’ for read, edit, and delete capabilities
  • hierarchical is it hierarchical, like pages
  • rewrite rewrites permalinks using the slug ‘webinar’
  • supports which items do we want to display on the add/edit post page

That’s the first simple step, and it should be enough to see your new custom post time in the WordPress admin. Save functions.php and take a look!

Step 2

The next thing we need to do is register a taxonomy. Or, in English, create categories for this new post type.

For example, in our webinars we are going to talk about some subjects and problems so in total we call it as taxonomy ‘Themes’.

It’s just one line of code:

register_taxonomy( "webinar_themes", 
                   array( "webinar" ), 
                   array( "hierarchical"   => true, 
                          "label"          => "Webinars Themes", 
                          "singular_label" => "Theme", 
                          "rewrite"        => true


The first item here is the taxonomy name — ‘webinar_themes’. It will be using in database and admin URL so it’s better not using spaces in the taxonomy name. The second is the name of the object type we’re applying it to, in our case the custom post type ‘webinar’ (which is an array). Finally our arguments; you can find a full list at the WordPress documentation about register_taxonomy(), and here we’re using just three which have the same meaning as described in Step 1.

Add that line of code in and you should now see:

How to Create WordPress Custom Post Type

You can then enter new ‘Themes’ just like you’d enter categories for blog posts. It looks like this:

How to Create WordPress Custom Post Type

Step 3

The third step is to add custom data fields to the add/edit post page.

For our webinars we can add things like the date when the webinar will happen and details on who will be a speaker, for who it is and its duration.

There’s a bit more code here, but read through it in order and it should make sense:

add_action("add_meta_boxes", "add_webinar_params_meta_box");

function add_webinar_params_meta_box()
    add_meta_box( "webinar_params-meta-box", 
                  "Webinar Details", 

function webinar_params_meta_box_markup($object)
    wp_nonce_field(basename(__FILE__), "meta-box-nonce");
            .webinar-row div
                padding-bottom: 10px;
            .webinar-row div:last-child
                padding-bottom: 0;
            .webinar-row label
                vertical-align: top;
                padding-top: 7px;
            .webinar-row input,
            .webinar-row textarea,
            .webinar-row label + fieldset
                width: calc(100% - 120px);
                max-width: calc(100% - 110px);
                display: inline-block;
            .webinar-row label + fieldset input[type="checkbox"]
                margin-right: 7px;
                width: 16px;
            .webinar-row label + fieldset label
                margin-bottom: 5px;
                width: 33%;
        <div class="webinar-row">
                <label for="webinar_when">Date<label>
                <input name="webinar_when" type="text" value="<?php echo get_post_meta($object->ID, "webinar_when", true); ?>">
                <label for="webinar_who">Speaker<label>
                <input name="webinar_who" type="text" value="<?php echo get_post_meta($object->ID, "webinar_who", true); ?>">
                <label for="webinar_for">Who is it for?<label>
                <textarea name="webinar_for" rows="5" cols="3"><?php echo get_post_meta($object->ID, "webinar_for", true); ?>
                <label for="webinar_long">Duration, minutes<label>
                <input name="webinar_long" type="number" value="<?php echo get_post_meta($object->ID, "webinar_long", true); ?>">
                <label for="webinar_youtube">Youtube Link<label>
                <input name="webinar_youtube" type="url" value="<?php echo get_post_meta($object->ID, "webinar_youtube", true); ?>">


First of all we call the add the add_meta_boxes function to the queue when the WordPress admin initialises, and within that function we add one meta box – place to enter our data. The context for that statement is

<?php add_meta_box( $id, $title, $callback, $page, $context, $priority ); ?>


Once the new meta box has been added it looks like this:

How to Create WordPress Custom Post Type

The final thing to do in step 3 is to make sure we then save these values with this post. I do this with:

add_action("save_post", "save_webinar_params_meta_box", 10, 3);


and the function:

function save_webinar_params_meta_box($post_id, $post, $update)
    if (!isset($_POST["meta-box-nonce"]) || !wp_verify_nonce($_POST["meta-box-nonce"], basename(__FILE__)))
        return $post_id;

    if(!current_user_can("edit_post", $post_id))
        return $post_id;

        return $post_id;

    $slug = "webinar";
    if($slug != $post->post_type)
        return $post_id;

    $webinar_when = "";
    $webinar_who = "";
    $webinar_for = '';
    $webinar_long = "";
    $webinar_youtube = '';

        $webinar_when = $_POST["webinar_when"];
    update_post_meta($post_id, "webinar_when", $webinar_when);

        $webinar_who = $_POST["webinar_who"];
    update_post_meta($post_id, "webinar_who", $webinar_who);

        $webinar_for = $_POST["webinar_for"];
    update_post_meta($post_id, "webinar_for", $webinar_for);

        $webinar_long = $_POST["webinar_long"];
    update_post_meta($post_id, "webinar_long", $webinar_long);

        $webinar_youtube = $_POST["webinar_youtube"];
    update_post_meta($post_id, "webinar_youtube", $webinar_youtube);


There’s nothing too tricky here. Again we’re adding an action, this time to the ‘save_post’ event. It fires the function save_details() which uses update_post_meta to save the relevant data.

Rewrite Problems?

There are a few reported problems out there about the rewriting of custom URLs; in short, they sometimes don’t work. However, if you’re experiencing this problem there are a couple of easy fixes.

First, simply go to the Settings > Permalinks page, which will flush the permalinks. This should clear up most problems related to permalinks, custom post type related or not.

If that doesn’t work you can add a line of code after you register the post type:




And there we have it! That is a whistle-stop tour of custom post types which gives you the appetite to explore further.

The best thing is to experiment with the code, read through the documentation in the WordPress Codex, and share your ideas with others. Like any new feature it will evolve as users create new and exciting ways to use it, and we’d love to hear about how you’re using custom post types here at comments block.


5 1 vote
Article Rating
Notify of
Inline Feedbacks
View all comments