Why and How To Use Custom Post Types in WordPress

Organizing folders in the same way WordPress organizes custom post types

WordPress sites are great at holding content. Got some words and pictures you want to show the world? WordPress is a great tool for that job. Probably the best. But by default it only has two types of content: Posts and Pages. These are great, by many people have many other types of content: client testimonials, documentation of a project, past projects listings and more. Posts and Pages can be made to house these sorts of items, but there’s a better option: WordPress custom post types.

Custom Post Types—often abbreviated CPTs—are ways you can get a Posts- or Pages-like interface for whatever type of content you desire. It doesn’t really matter what you call a CPT you create or what you use it for, the limits are your imagination. So we’ll cover all the aspects of how you do this today!

One quick thing before we get started. If you’re looking to learn WordPress development, we’ve written the best guide to it out there:

The Best Way to Learn WordPress Development

Get Up and Running Today!

Up and Running is our complete “learn WordPress development” course. Now in its updated and expanded Third Edition for 2018, it’s helped hundreds of happy buyers learn WordPress development the fast, smart, and thorough way.

Here’s what they have to say:

“I think anyone interested in learning WordPress development NEEDS this course. Watching the videos was like a bunch of lights being turned on.” -Jason, WordPress developer

“Other courses I’ve tried nearly always lack clear explanations for why WordPress does things a certain way, or how things work together. Up and Running does all of this, and everything is explained clearly and in easy-to-understand language.” -Caroline, WordPress freelancer

Why You Should Make Custom Post Types in WordPress

In building your site, you’re often constrained  by the limitations of the WordPress default “Post Types.”

Why custom post types? Because when building sites, you’ll sometimes find that you’re constrained either metaphorically or practically by the limitations of the WordPress default “Post Types”—which means both “Posts” and “Pages”. In some situations, this will simply be that you’re looking to create something clearly different than either of those things. “Forms” is a common example: even if you mangled a default post type to serve the purpose, the concept just doesn’t fit well.

Another place that the need for new “post” or content types reveals itself is when you’ve created a deeply nested hierarchy within your default Pages content type to get the kind of permalinks you want. When we made our agency site the first time, this was how we made a listing of all the different services we offered. I now think making those pages was a waste of time, but when we did it, we had 30-some items nested under the “Services” page. And it worked, but it wasn’t fun to work with. I’d advise if you have more than 10 pages nested under a particular parent page within WordPress it’s a place you could benefit from making a new content type.

Aside from “too many pages”, there are some other signs that you’re probably best served by a custom post type:

  • If you find that you’re regularly using a particular Post Category of your blog in strange ways, and consequently find yourself banging your head against the logic in your template files to make your posts look like you want, you probably want to create a different content type for that
  • If you’ve been forced to “hack” your permalink structure to be sensitive to the category of a post to give your posts in that special category a sane URL, it’s again probably a case where you should use a Custom Post Type.

How We Use CPTs on WPShout

We initially published this article in 2013. Since then, we’ve create a number of different custom post types here on WPShout to house different types of content. I’m going to briefly highlight them, so you’ll better understand what CPTs look like in action.

  • Posts (the default ones) — These are what we publicly call “Articles”. They’re the every-Tuesday long-form written content you’ve come to expect from us.
  • Courses — These are where we gather together a variety of articles into a longer-form post which includes links to other relevant articles. The idea is its your single-stop place to learn a topic in greater depth.
  • Quick Guides — In the last few years, we realized there’s a need in our audience for shorter-format posts that are specifically about how to accomplish a particular goal. Quick Guides started as a category of “Posts” but we realized they were better outside of it.
  • Links — These also started as “Posts”, but have since moved to their own CPT. Links are content that’s outside of the site that we think our audience might want to see and would benefit from hearing about. We use the “posts” in that CPT to say more, and to route that content into our email newsletter.

In addition to these CPTs, we do have some plugin that have created their own CPTs on our site. Those are a giveaways plugin we’re using and a forms plugin we’re using.

You almost certainly don’t run a site just like WPShout, but I hope that explaining that helps you undertand what sort of content types might make sense for you.

So You Need Custom Post Types. How Do You Get them?

Once you’ve identified a need for a new content type on your WordPress site, there are a lot of ways that you can create them. Too many for me to cover them all exhaustively here. But, they break down into three basic ways to get your CPT(s), so I’ll explain those.

Using a Plugin that Already Creates Them (and hopefully does more)

woocommerce logoThis one was omitted in the first publishing of this article, but I think it’s really important and good for people to realize. As I suggested in the previous section, plugins create their own custom post types. The example that I think makes the absolute most sense to discuss is an ecommerce plugin like WooCommerce (as the biggest one on the block, it’s what we’ll talk specifically about).

WooCommerce will create (and show) you a new custom post type called “Products”. It’s a great example of a place where custom post types make such perfect sense. Imagine that you were creating an ecommerce site you had to use a section labelled “Pages” or “Posts” to control you products.

WooCommerce’s Product custom post types houses some custom functionality and also a bunch of the stuff you’ve come to expect. They support the content editor, featured images, and more. They also have their own taxonomies like “product category” and more. All the features WooCommerce’s product post type uses is available to you as a developer when you make your own CPTs and interface. That said, you should also realize that you don’t have the thousands of hours that have gone into making WooCommerce as good as it is. For that reason, maybe don’t go off creating a “Product” custom post type when you could just use WooCommerce and get all those goodies for free.

User-interface-based Plugin Systems for CPT Creation

Beyond just using a plugin that supports the custom post type you want, there are plugins that let you set up your own. The big plus, you can create CPTs without writing any code. These plugins will help you make CPTs in the admin-area of your site by just filling out a form. These are where you’d want to start if you’re coming at the problem as a user who doesn’t want to write any PHP.

Any of these systems will create a new set of options for you in your WordPress dashboard that allows you to create these new content or “Custom Post Types” by simply typing in its name and clicking a few little options. Many of them even give you a nice interface to set up some custom fields and taxonomies on those new types of content you’re creating (but delving into that’s a topic for another time).

As I said, there are a huge number of plugins in this category. Here are a few of the free plugins in the WordPress plugin repository that’ll do this, with a quick summary:

  • logo-pods-headerPods — Not only can you create custom post types, and taxonomies, but you can also use pods to augment default post types and user profiles. Pods is community-driven, supported by a non-profit foundation, and gets some support from Automattic. (We have a Quick Guide on doing this with Pods!)
  • Toolset Types — Create your custom post types, and taxonomies and more. It also has some premium extensions to enhance its and WordPress’s functionality.
  • Custom Post Type UI — From the fine folks at WebDevStudios, a simple plugin to let you create custom post types and taxonomies for them.
  • Custom Post Type Maker — Very much like Custom Post Types UI, this gives you a simple interface to creation post types and custom taxonomies.

Create a Custom Plugin for Your Content Type

custom-post-type-code

Yeah! That’s the stuff!

As someone who would rather write PHP than fiddle with interfaces, I typically skip that whole class of solutions when I’m looking to create new Custom Post Types in WordPress. WordPress’s register_post_type() function is how you create new content types with PHP. It’s relatively easy to use if you have an understanding of the WordPress API and hooks system. If that was all Greek to you, the first class of options will probably serve your needs well. They’re using this function under the hood, but you don’t have to mess with it.

My preference for this method is a little more justified than just disliking clicking buttons on user interfaces I didn’t design. Because all of the custom-post-type plugins above are letting you quickly and easily set up your post types, you’re storing your data about the post types (and taxonomies and custom fields, if you’re using them) in the database as well. This has two big drawbacks:

  1. Your setup is now less portable. A customized plugin which is all set up for the specifics of your content types and features can easily be taken to another WordPress site without any need to transfer database data. Again, for most people looking at the first type of solution, this isn’t a real problem. It’s an issue for a developer working on projects which may have multiple intermediary sites in the design and development process, but if you only have your “one true site” it’s a non-issue. (But, if you only have your “one true site”, I really hope you keep at least one backup system running for your site.)
  2. Because the “do it for me” plugins are storing the data in the database, they’re making at least one unnecessary database call (to get your setup options) before each WordPress page load. For high quality and performant hosting setups, this isn’t a big deal, I’ll admit, but it’s not free. I’ve not done benchmarks, nor do I intend to, but this is a cost of these plugins.

Example Code of a Custom Post Type, Explained

This tutorial won’t get super-exhaustive into the code for CPTs. I don’t think you want to read that nor do I want to write a line-by-line breakdown of sample code and every parameter to the register_post_type function. But I do think there are some intuitions I have that I’ll offer you about this example code, borrowed from the very-good and very-thorough Codex page:

add_action( 'init', 'codex_book_init' );
function codex_book_init() {
	$labels = array(
		'name'               => _x( 'Books', 'post type general name', 'your-plugin-textdomain' ),
		'singular_name'      => _x( 'Book', 'post type singular name', 'your-plugin-textdomain' ),
		'menu_name'          => _x( 'Books', 'admin menu', 'your-plugin-textdomain' ),
		'name_admin_bar'     => _x( 'Book', 'add new on admin bar', 'your-plugin-textdomain' ),
		'add_new'            => _x( 'Add New', 'book', 'your-plugin-textdomain' ),
		'add_new_item'       => __( 'Add New Book', 'your-plugin-textdomain' ),
		'new_item'           => __( 'New Book', 'your-plugin-textdomain' ),
		'edit_item'          => __( 'Edit Book', 'your-plugin-textdomain' ),
		'view_item'          => __( 'View Book', 'your-plugin-textdomain' ),
		'all_items'          => __( 'All Books', 'your-plugin-textdomain' ),
		'search_items'       => __( 'Search Books', 'your-plugin-textdomain' ),
		'parent_item_colon'  => __( 'Parent Books:', 'your-plugin-textdomain' ),
		'not_found'          => __( 'No books found.', 'your-plugin-textdomain' ),
		'not_found_in_trash' => __( 'No books found in Trash.', 'your-plugin-textdomain' )
	);

	$args = array(
		'labels'             => $labels,
                'description'        => __( 'Description.', 'your-plugin-textdomain' ),
		'public'             => true,
		'publicly_queryable' => true,
		'show_ui'            => true,
		'show_in_menu'       => true,
		'query_var'          => true,
		'rewrite'            => array( 'slug' => 'book' ),
		'capability_type'    => 'post',
		'has_archive'        => true,
		'hierarchical'       => false,
		'menu_position'      => null,
		'supports'           => array( 'title', 'editor', 'author', 'thumbnail', 'excerpt', 'comments' )
	);

	register_post_type( 'book', $args );
}

The first part of this code that’s worth noting is the add_action line. If you’re new to WordPress, this can be pretty confusing. We’ve got a pretty thorough tutorial about this topic too, WordPress Hooks, Actions, and Filters: What They Do and How They Work. The executive summary is that line is what triggers all the other code in the snippet to run.

The second thing to discuss is the first large array of labels. These aren’t, strictly-speaking, necessary. I find them annoying to write, but I like the effect of them so I typically keep them around. Their role is to give WordPress the right text for all your interface buttons. So if you don’t have the edit_item array element, for example, when you’re viewing an instance of the post type on the front side of your site, the admin bar will say “Edit Post” rather than “Edit Book”. It’s not the end of the world, but it’s not great. If you want WordPress to use the right language, you need to include that language.

Finally, there’s the whole $args array. This is a common (and to my mind, good) pattern in WordPress. It’s useful because it give you the option to specify some elements for the configuration, but you don’t need to write them all. This example is very specified. The one element of it I really want to highlight is rewrite which is where you define the URL slug for the elements. It is this element, not your first argument to the register_post_type function, which controls what you’ll see in URLs. For post types that are two English words, or where you want the internal WordPress code name for it to be abbreviated from the way you want to use it in your URLs, these would be different.

On the note of URLs, another small “gotcha” of custom post types is that you must refresh your rewrite rules or permalinks for them to show correctly. This is pretty easy to do, just go to “Settings > Permalinks” and temporarily set a different value, then change it back. That saving (not actually the change, I’m just superstitious) is what will allow your CPT pages to start working.

Custom Post Types Help WordPress Serve Your Site Better

Custom post types are a powerful and important feature that take WordPress from “blog software” to a full content-management system. I find the use of “post” in the name “Custom Post Types” unfortunate, but it’s just an accident of history that quickly falls into the background. The core power of CPTs is great enough you quickly forget it. Whether Pods, WooCommerce, or your own custom plugin gives you the custom post types you need, your WordPress site will be better when it has them. Happy hacking!

Image credit: jodimichelle


8 Responses

Comments

  • Nice post thanks for the recommendation of plugins. I have used Types before which is great but wasn’t familiar with the others will check them out.

  • For the past few days, I have been researching on Custom Post Types and whether it is good for my site. I have decided not to go ahead as it is still not mature enough and involves too much of hacking with WP/theme code.

    The permalinks are still not sorted out as the permalink settings do not apply to the custom post type. Even if category is enabled, there is no simple way to get the category name in the URL.

    • Hitman007 says:

      I’m having the same thought! I am coding several sites with a bunch of different custom post types. I’m using them for perfectly good CMS reasons. They are things like “Products” and “Customer Orders”. It’s a nightmare! Permalinks broken, bad documentation. I’m going back to using regular posts, and putting anything else I need into the post meta data and using private functions to manipulate the data. I started using WordPress hardcore about two years ago, and I wish I had focues on Rails or something else. 🙁

  • Are there any disadvantages to CPT’s with how the structure is read?

  • Good to see a mention of Pods.

  • Nils says:

    Thank you for the nice article!

    I used Types till now, but I think I will start to try to create Custom Post Types in WordPress by myself and your example code is a good help.

  • Brian says:

    Hi David,

    Great article.

    Custom Post types are much needed if anyone wants to extend the capabilities of WordPress. I’m planning up for a coupon site and I definitely need CPTs.