Andy from Webcrunch

Subscribe for email updates:

Mastering WordPress Custom Post Types
Portrait of Andy Leverenz
Andy Leverenz

July 20, 2015

Last updated November 5, 2023

Mastering WordPress Custom Post Types

WordPress is everywhere. More than 25% of websites are built on the CMS that is highly customizable. Being free and completely configurable explains the popularity and many designers and developers use it for creating their websites or their clients websites, not to mention all the documentation and resources available for WordPress today.

In early 2005, the team at Automattic expanded on the platform by introducing custom post types to the world. With these added features, designers and developers could now craft WordPress to be whatever it is they wanted. This post dives deep on custom post types, why we use them, and what makes them so powerful. I’ll discuss history, best practices, and provide a real-world example.

Download source code


A little history lesson before we get into the basics will probably help you understand where and why custom post types came to be. Custom post types are said to have shifted WordPress from a blogging platform into a full-blown content management system. History proves this as you’ll read below.

In the beginning, WordPress 1.0 contained a function bundled with the platform called wp_insert_post(). This function did what you expect and inserted the post. A no brainer…

By the time, WordPress 1.5 rolled out you could now pass a parameter to define which type of post you wanted to output by using a post_type value in the function. Obviously you needed to create more constraints and enable the functionality with the code but the potential was there if you wanted it.

WordPress 2.9 finally introduced the register_post_type() function to the public. No longer were hacks and tons of advanced code was needed to define a custom post type. Awesome!

With WordPress 4.2 already here we can now utilize easy to install plugins to do a lot of the heavy lifting for us but being a perfectionist I still prefer to create my own for the sake of knowing exactly what is going on inside of WordPress.
There are some great code generators available to help you save some time with this as well.

What is a custom post type?

Custom post types are just posts. They are like any other post inside WordPress, but the main difference is in their definitions and parameters. If you wanted to separate different chunks of posts into a new area on your website you could do so by creating a custom post type. Inside of called them posts you could call them projects for example. Some common examples I have used are:

  • Reviews
  • Projects
  • Portfolio Pieces
  • Listings
  • Case Studies
  • and so many more

Why use a custom post type?

You would use a custom post type when you need to divide up the content on your website. Rather than just a typical blog you can create new posts that are of different content and focus. Doing this allows you to easily maintain the content as well as add new content in the future using the WordPress backend. Rather than editing code and updating every time you can use what WordPress was built for by offering a dynamic approach to creating and maintaining a website.

Say you wanted to create a portfolio section for your creative agency’s website. You could do this all from scratch but wouldn’t it be better to tie into the power of WordPress and use a custom post type for each new project you add? Doing this is relatively simple.

To start, I would create a custom post type called projects. Doing this would allow us to create a new area on our website that would be filtered and output strictly for this type of content. To put it in perspective you would end up with a URL like You could then post projects as projects or even go one step further and have different post types or taxonomies within the projects post type. Sounds confusing doesn’t it? It’s not that bad once you get the hang of it. I’ll show you a step-by-step approach later in the post so keep reading!


I mentioned before that plugins can take care of creating these post types for you. There are a number of them available that offer both advanced and simple setup depending on your needs. I personally don’t like to rely on too many plugins in my arsenal. It’s not that they aren’t powerful and future proof, but there are times where I need to get extra technical with my own code and doing this with a plugin just doesn’t work. A couple of good plugins to try are:

Custom Post Type UI

The Custom Post Type UI plugin provides an easy to use interface that allows you to create and administer custom post types and taxonomies in WordPress. The plugin simply creates the types. You will need to add them to the theme yourself.


Types lets you customize the WordPress admin area by adding content types, custom fields, and taxonomy. You will be able to craft the WordPress admin and turn it into your very own content management system.

Creating Your Own Custom Post Type

Before getting started, I must state that adding a custom post type requires additional code inside your theme or installed as a plugin. If you find yourself needing to use your custom post type on multiple websites or different themes then I suggest you create a plugin out of the code below. Doing this is very easy but, unfortunately, isn’t the scope of this post. I’ll be sure to write about how to create a WordPress plugin in the very near future.

Getting Up and Running

If you’re new to WordPress and aren’t versed in PHP yet then I invite you to follow along but also try and see if a plugin is suitable enough for you. I’ll be working on a local WordPress installation. My environment is a Mac and at this time I’m running WordPress 4.2.2 with MAMP. I wrote another post on How to install WordPress locally or on your server if you need direction on how to do so.

For demonstration purposes I’ll be using a child theme based off the default Twenty Fifteen theme.

Child Theme Setup

By default WordPress comes with the Twenty Fifteen theme. To get started I want to create a child theme of the Twenty Fifteen theme so that if there are future updates any changes I make won’t get overwritten. Most people don’t know how powerful child themes can be and if you’re using a theme you didn’t produce, creating a child theme is an absolute must.

Create a new folder called twentyfifteen-child in the wp-content/themes directory.

Create a new folder for your child theme Create a new folder for your child theme

inside this folder we need a stylesheet and a new functions.php file to create our custom post types. Create both of those files now. Now our file structure looks like this:

Inside our style.css file we need to add some data to let WordPress know it exists and that it’s also extending from the default twentyfifteen theme as a child theme. The code you enter is up to you but it should look very similar to the commented text below. Add it at the top of your style.css file.

 Theme Name:     My Custom Post Type Theme
 Theme URI:
 Description:    A demo theme for authoring custom post types
 Author:         Andy Leverenz of Web-Crunch
 Author URI:
 Template:       twentyfifteen
 Version:        1.0.0

@import url(‘../twentyfifteen/style.css’);

The important things to note is that we are importing the CSS file from the default twentyfifteen theme and then allowing ourselves the ability to customize any additional styles that won’t be overwritten in our child-theme. Be sure to verify the file path as it will break the theme’s presentation layer otherwise.

Pay attention to the Template: declaration as well because this needs to be the name of the theme you are creating the child theme from.

If you navigate to your WordPress dashboard and go to Appearance/Themes you should be able to activate the child theme. I copied the thumbnail over to the child theme directory. You can add your own custom thumbnail if you please.

screenshot of child theme activiated

Now we have the ability to adjust any styles in the theme without the fear of being overwritten.

Up next we need to pay attention to our functions.php file. Inside this file we will create and initialize our custom post type. The code we input will allow us to add custom post types from the backend of the WordPress dashboard. Later we will create a new template for the new custom post type which will then output the content we are striving for.

Creating the custom post type

Navigate to your functions.php file and enter the code below.


if ( ! function_exists(‘projects’) ) {

// Register Custom Post Type
function projects() {

    $labels = array(
        ‘name’                => _x( ‘Projects’, ‘Post Type General Name’, ‘text_domain’ ),
        ‘singular_name’       => _x( ‘project’, ‘Post Type Singular Name’, ‘text_domain’ ),
        ‘menu_name’           => __( ‘Projects’, ‘text_domain’ ),
        ‘name_admin_bar’      => __( ‘Project’, ‘text_domain’ ),
        ‘parent_item_colon’   => __( ‘Project:’, ‘text_domain’ ),
        ‘all_items’           => __( ‘All Projects’, ‘text_domain’ ),
        ‘add_new_item’        => __( ‘Add New Project’, ‘text_domain’ ),
        ‘add_new’             => __( ‘Add New’, ‘text_domain’ ),
        ‘new_item’            => __( ‘New Project’, ‘text_domain’ ),
        ‘edit_item’           => __( ‘Edit Project’, ‘text_domain’ ),
        ‘update_item’         => __( ‘Update Project’, ‘text_domain’ ),
        ‘view_item’           => __( ‘View Project’, ‘text_domain’ ),
        ‘search_items’        => __( ‘Search for a project’, ‘text_domain’ ),
        ‘not_found’           => __( ‘No projects found’, ‘text_domain’ ),
        ‘not_found_in_trash’  => __( ‘No projects found in Trash’, ‘text_domain’ ),
    $rewrite = array(
        ‘slug’                => ‘projects’,
        ‘with_front’          => true,
        ‘pages’               => true,
        ‘feeds’               => true,
    $args = array(
        ‘label’               => __( ‘projects’, ‘text_domain’ ),
        ‘description’         => __( ‘A custom project post for the portfolio section of the website’, ‘text_domain’ ),
        ‘labels’              => $labels,
        ‘supports’            => array( ‘title’, ‘editor’, ‘excerpt’, ‘author’, ‘thumbnail’, ‘trackbacks’, ‘revisions’, ‘custom-fields’, ‘page-attributes’, ),
        ‘taxonomies’          => array( ‘category’, ‘post_tag’ ),
        ‘hierarchical’        => true,
        ‘public’              => true,
        ‘show_ui’             => true,
        ‘show_in_menu’        => true,
        ‘menu_position’       => 5,
        ‘menu_icon’           => ‘dashicons-art’,
        ‘show_in_admin_bar’   => true,
        ‘show_in_nav_menus’   => true,
        ‘can_export’          => true,
        ‘has_archive’         => ‘past_projects’,
        ‘exclude_from_search’ => false,
        ‘publicly_queryable’  => true,
        ‘rewrite’             => $rewrite,
        ‘capability_type’     => ‘page’,
    register_post_type( ‘projects’, $args );


// Hook into the ‘init’ action
add_action( ‘init’, ‘projects’, 0 );



The code above is fairly straight forward but it is a little advanced. If you read through it you’ll start to get the idea that we are just defining built in parameters to both create the custom post type as well as craft the UI on the WordPress backend. The function itself is called projects. Within it I’ve defined how I want it to appear and operate. If you head to your WordPress backend you should now see a new projects within the interface.

At this stage we have the interface working and initialized but if I went to create a new project nothing would output into our theme. To test in the near future we do require some content so I’m going to add a couple projects so that when we incorporate it into our theme there is something to output. If you’re following along find the images and content I’ve imported in the demo files or just use your own.

Outputting the custom post type data

If you go to try to view the post you’ll get a Page Not Found error which is what we are expecting. Nowhere in our theme is there a way for the new post type to output yet so we have to add this functionality. To do this in a child theme we need to create a template for the new post type. Let’s do that now. In your child-theme directory create a file called projects-template.php. Inside the file add the comment and PHP code below:


Template Name: Projects Template


Create a Projects Page
The next step is to create a page called Projects in WordPress so that we can output our new post type onto it. Doing this requires us to assign the new template to our page which you should now see in the template selection area on the lower right hand site of the page.

Create a new page called "Projects" and assign our newly created project page template to it. Create a new page called "Projects" and assign our newly created project page template to it.

Great, now we have a new page with a new template. If you navigate to the Projects page you’ll see a white screen. Don’t worry this is what we want. The template allows us to break away from the default page structure of WordPress if we want. Doing this allow you to customize the experience which for custom post types is what we want.

Let’s setup the template to get the theme displaying correctly to star things off. Inside projects-template.php update the code to look like this:



Template Name: Projects Template


get_header(); ?>

    <div id=“primary” class=“content-area”>
        <main id=“main” class=“site-main” role=“main”>

        // content will go here

        </main><!— .site-main —>
    </div><!— .content-area —>

<?php get_footer(); ?>

screenshot of updated template Updated template

The theme should be displaying at this point. You’ll notice we don’t have any post yet though. This is because we are missing the back bone of WordPress which is the loop. Let’s add that now. Add the code below inside the main container.

 // Start the loop.
 while ( have_posts() ) : the_post();

 // Include the page content template.
 get_template_part( ‘content’, ‘page’ );

 // End the loop.

Here we have added the famously known WordPress loop which is looking for any new posts we create inside our database. Inside the loop is a get_template_part function which allows us to use different templates for each page. You can create your own template and include here as well. I’m going to create a new template called project. It will be named content-project.php. Add it to your child theme directory and change the code above to this:

  // Start the loop.
  while ( have_posts() ) : the_post();

    // Include the page content template.
    get_template_part( ‘content’, ‘project’ );

  // End the loop.

If you preview the page you should still see no posts coming through. In order to get the custom post types to display we need to create a new query which will allow us to find the post type called projects that we created before. All we are doing is creating a couple variables and a new query and then passing it through the WordPress loop. The updated code is a little advanced but looks like this:

  // set custom post type name to a variable
  $args = array(‘post_type’=>’projects’);

  // set a variable to use to pass the arguments into a new WP query
  $loop = new WP_Query($args);

  // Display the contents of the query
  while ( $loop->have_posts() ) : $loop->the_post();

    // Include the page content template.
    get_template_part( ‘content’, ‘project’ );

  // End the loop. Reset Query


See the comments above to get a better idea of what’s going on. Essentially we are looping through all posts to find the ones with the post type of projects. Those posts are then fed through our template and output on the page.

Customizing the output

Chances are you will want to customize the content that gets output from the loop we wrote above. You can do so by modifying code that goes within the content-project.php file we created. I customized mine by copying and pasting the code from the default twentyfifteen parent theme file called content-page.php. From there I tweaked it a bit to display the data I wanted.

Our content-project.php file ends up containing the code below. Check the demo files if it’s easier for you rather than copy and pasting from this post.

<article id=“post-<?php the_ID(); ?>” <?php post_class(); ?>>
    // Post thumbnail.

    <header class=“entry-header”>
       <?php the_title( ‘<h1 class=“entry-title”>’, ‘</h1>’ ); ?>
    </header><!— .entry-header —>

    <div class=“entry-content”>
    <?php the_excerpt(); ?>
    wp_link_pages( array(
      ‘before’      => ‘<div class=“page-links”><span class=“page-links-title”>’ . __( ‘Pages:’, ‘twentyfifteen’ ) . ‘</span>’,
      ‘after’       => ‘</div>’,
      ‘link_before’ => ‘<span>’,
      ‘link_after’  => ‘</span>’,
      ‘page link’    => ‘<span class=“screen-reader-text”>’ . __( ‘Page’, ‘twentyfifteen’ ) . ‘ </span>%’,
      ‘separator’   => ‘<span class=“screen-reader-text”>, </span>’,
      ) );
    </div><!— .entry-content —>

<?php edit_post_link( __( ‘Edit’, ‘twentyfifteen’ ), ‘<footer class=“entry-footer”><span class=“edit-link”>’, ‘</span></footer><!— .entry-footer —>’ ); ?>

</article><!— #post-## —>

After adding some new projects you should now be able to see the working result of our integration. Here’s a few screenshots of what I’ve built so far to give you an idea.

WordPress Project Custom Post Type Section and UI

screenshot of new custom post type in wordpress

Projects Landing Page

projects landing page

Indvidual Project Page

individual project page

Taking things further

Your custom post type can be of virtually any type. You can take things further by having a custom post type within a custom post type or having categories or taxonomies that correspond with each. The possibilities are really endless and this is why custom post types are so powerful. You can create anything from a portfolio to a review or rating system, or even a custom portal for that new web app you’ve been thinking about building.

From here I invite you to take what I’ve shown you and build upon it. Introduce it to your own themes and applications and see how you can make your website more intriguing and powerful to your users. Good luck with your journey and if you have questions feel free to leave a comment!

Link this article
Est. reading time: 16 minutes
Stats: 1,039 views


Products and courses