Before getting into how to create and work with custom post types, we should clarify some terminology since the naming convention results in a bit of confusion.
It is best to think of a post as a unit of content stored in the database. Each post has a post_type property, which defines what type of content it is. Here’s where things get tricky. One of the most commonly used built-in post types is named “post.”
This means that “post” can refer to any post entered into WordPress (whether this is a blog post, a page or a custom type), but it can also refer to the regular ol’ blog post.
Custom Post Types
Essentially, a custom post type is nothing more than a change in the
post_type attribute of a post. If you publish a blog post on your website and go into the database to rewrite the post type from “post” to “page” the post will now show up in the pages section in the admin.
WordPress defines a bunch of properties for each post type. Is the particular post type searchable? Is it visible in the admin? Can categories and tags be assigned to it, does it allow comments? A number of these features can be set for each custom post type.
So what is a good example of a custom post type? Say you love to cook. Chances are you have a website where you write about the things you are interested in, but now and then you publish a recipe. A recipe is a very different type of content than a personal blog entry. In this case, it may be appropriate to create a “recipe” post type.
When To Use Custom Post Types
It may be difficult to figure out when a custom post type is needed. Sometimes you can get by with using categories. In our example, we could have simply created a “Recipes” category for blog posts.
So where’s the line? When should we use categories and when are custom post types more appropriate?
There really isn’t a set-in-stone rule here. It comes down to practicality, personal preference and how your theme is built. There are some good guidelines though; if it seems like some of these apply to you, a custom post type might be in order:
- If you publish at least two very different types of content. For example, personal blog entries and recipes.
- If it would be better to visually and structurally distinguish a specific type of content. For example: personal blog and your illustration portfolio.
- If a type of content doesn’t fit in a chronological order. For example, a company blog and company style guides.
- If a content type could easily be separated out into a different website and still remain coherent. For example, a personal blog and sold products.
- If using categories and tags would lead to over-complicated taxonomies. For example, a personal blog and movie reviews.
Built-In Post Types
Now that we understand a bit about custom post types, let’s take a look at the post types WordPress uses by default. Many people know about posts (
post) and pages (
page) but did you know that uploaded images are also posts? They use the
attachment post type.
Surprisingly enough there are two more: revisions (
revision) and navigation menus (
nav_menu_item). Revisions are just like posts but they contain the data about past versions of posts. Navigation menu items hold information about each separate item in the navigation system.
Creating A Custom Post Type
Enough talk! Let’s actually create a custom post type. All the code in this article is best placed in a plugin. If you’d like to just give it a quick go you can place it in your theme’s functions file, but I advise moving it to a plugin for production use.
To create – and highly customize – a post type you would only need a single function:
register_post_type(). The documentation for it is pretty hefty but allows for some great modifications. To register a very simple custom post type you’ll only need a couple of lines:
Let’s analyze the basics. Notice that
register_post_type() is used in a function that is hooked to the
init action. It takes two arguments: the custom post type and an arguments array. The custom post type should be 20 characters at most and must not contain any spaces or capital letters. I also recommend writing singular forms (post, page, recipe, book, etc.). We’ll be looking at the arguments in a detail soon.
Customizing Post Types
There are three main things you can do to customize your post types:
- Modify the arguments of the
- Add custom interaction messages (post deleted, updated, etc.)
- Adding help sections to various screens in the custom post type admin
Let’s take a look at the most common arguments you can change when you register the post type and then turn our attention to the interaction messages and help text.
Description and Labels
If you use the simple method of registering a post type you’ll notice that buttons still say things like “New Post” and “Delete Post”. These can be customized using the
labelsproperty of the argument array.
Note that in the previous example we used the
label property, this example uses
labels. Make sure to use translation functions if your work will be for public consumption.
Post Type Visibility
There are a number of parameters which allow you to fine-tune the visibility of your custom post type in the front and backend. The most prominent one is the
publicparameter, which sets the values of other properties in one go.
If it is set to
true, the post type will be included in searches, the UI will be shown, it will show up in the admin bar and so on. This is similar to how built in pages and posts work.
If it is set to
false, the post type is excluded from searches, it will not show up in the UI, it will be hidden in the menus and so on. This is like the built-in revision post type.
For more granular control you can specify properties separately. The value of all the parameters below is the same as the value of the
public parameter, except for
exclude_from_search. The value of this property is the opposite.
This example creates a post type which holds our notes on customers. We probably don’t want this to be visible in any way on the front end so I’ve made sure the
public property is false. We do want to be able to manage them in the backend so I’ve set the
show_in_admin_bar properties to “true.”
You can use three functions to modify the behaviour of the menu entry for your custom post type.
show_in_menu sets where the menu is shown. If set to false the menu entry is not shown. If set to true it will be shown as a top level menu. You can set it to an existing top level page like
upload.php to add it as a sub-menu.
menu_position property sets where the menu shows up in the top-level list. Take a look at the Codex for the numbers to use for specific placements.
menu_icon parameter allows you to set an icon. You can add a URL to an icon, or you can use the name of an icon from Dashicons which now ships with WordPress.
The code above will add our post type as a top level menu entry into position 20 (which is just below pages) and It will use the carrot icon from the Dashicons set.
Configuring Post Type Features
You can choose a number of features to use or discard for your custom post type. The
hierarchical property will create a flat structure (like posts) when set to false. If set to “true,” you will be able to create parent-child relationships like you can with pages.
taxonomies property allows you to assign custom taxonomies to the post type. This is an array of taxonomy slugs. The following example creates a hierarchical post type with support for tags.
If you plan on using custom taxonomies you will still need to create the taxonomy with the register_taxonomy() function.
supports property holds an array of features which the post type supports. These have an effect on the admin user interface and on some parts of the front end as well. Here’s a list of available options:
Archives And Rewrites
has_archive is a great property which allows you to create a listing of your post type on the front end automatically. By setting the value to
true you’ll find a list of your custom posts at http://yourdomain.com/post_type/. In your theme you can customize this listing using the
A full guide to rewrites is a bit out of the scope of this article, but it’s handy to know about them. The
rewrite property defines how post type URLs should be handled. A good use-case is if you are creating a post type for a common task, say products. To make sure your plugin doesn’t conflict you could use “my_product” as the post type and rewrite it to “product” in the URL.
Post Type Interaction Messages
Whenever you perform an action on a post (saving, deleting, searching, etc.) you receive messages which give you feedback about your action. These messages can be tailored to the post type using the
post_updated_messages filter, here’s how:
First of all, note that the
$messages variable passed to the function contains all messages. The sub-arrays contain the messages for specific post types. All we need to do is define an array for the custom post type with the appropriate messages. Don’t forget to use translation functions on those messages. I’ve left them out here for brevity’s sake.
Ever noticed the help tab in your posts or pages section? If you click there you’ll see that you can add a great little help section split into tabs. Adding contextual help is extremely important as it allows users to get help on the spot. This is better for them and better for you as well – the fewer support requests you get, the better.
We’ll need to use the
$screen object in the function we hook to
admin_head. The template for adding help sections is quite straightforward.
Here we go:
The first thing we do is check the current screen. If we are not on the main post type screen we return early. If we are on the correct screen we can create our help tabs. Each help tab consists of a unique ID, a unique name and the content of the tab. These can then be registered individually with the
Putting It All Together
The code to register a recipe post type, along with interaction messages and help sections would look something like the example below. Don’t forget the settings you use are ultimately up to what your project requires.