Download today

WordPress Development for Intermediate Users: Custom Post Types and Taxonomies

Custom post types and taxonomies are what make WordPress a Content Management System (CMS), and not just a blogging platform.

With them, you can add your own post types, which you can then display using targeted template files. You can also create custom taxonomies that let you use more than just categories and tags to classify your content.

This is the fourth post in our WordPress Development for Intermediate Users series. This series follows on from our popular WordPress Development for Beginners tutorials, which introduced you to the fundamentals of developing websites with WordPress, how to get started coding with PHP, and building themes and plugins.

So far in this series, you’ve created your own theme and plugin and now you’ll move on to adding custom post types and taxonomies to your site. In this tutorial you’re going to learn:

  • What custom post types and taxonomies are, how they differ from the ones you get with vanilla WordPress, and how they can help you.
  • How to register custom post types and taxonomies in a plugin.
  • How to add a taxonomy to (or remove one from) a post type.
  • How to display custom post types and taxonomies, including via template files.
  • Which functions you can use to fetch posts using your custom post types and taxonomies.

Let’s start with an overview.

That’s right, tons of WordPress knowledge, peer review, instructor feedback and certification, for free for WPMU DEV membersStart Learning

Post Types and Taxonomies in WordPress

WordPress comes with a number of post types and taxonomies already included. The post types are:

  • Post – the posts you use for your blog.
  • Page – static pages.
  • Attachment – media files.
  • Revision – each revision for each post is stored as a post of the ‘revision’ post type.
  • Navigation menu item – every item in your navigation menus is stored against this post type.

The word “post” can be a bit confusing when it comes to WordPress. Let’s take a look at some of the terminology, and try to get it straight:

  • post (with a lower case “p”) – in the database, WordPress stores every item of every post type as a post. So if you create a post type called product and then create products for your site, they go in the wp_posts table of the database in the same way a standard post would. The difference is that the post type stored against that post as a value in the database would be product instead of post.
  • Post – this is a post type, in the same way that any custom post type you create is a post type. A Post is the post type that comes with WordPress for blog posts, updates, news items etc.
  • post type – this refers to any post type (with a little “p”) including Pages, Posts etc. Don’t confuse it with post formats – post format is a taxonomy that’s used to classify Posts, as you’ll see shortly.

In this part of the series, I’m going to use a capital P to refer to Posts in the context of blog posts, and a lower case p to refer to posts in the more general sense, including posts of any post type. I’m not doing that in the rest of the series though as it’s not the usual way to write, so please don’t get confused!

Phew! So that’s post types. What about taxonomies? Well, WordPress also comes with some of these out of the box:

  • Category – a hierarchical taxonomy (meaning you can nest categories inside each other) used for Posts.
  • Tag – a non-hierarchical taxonomy used for Posts.
  • Link Category – used to classify links, if you’re using the old “blogroll” function. This is disabled by default so I wouldn’t worry about it if I were you.
  • Post Format – another taxonomy for classifying Posts, which isn’t hierarchical. You can use this to display different post formats differently, using the relevant template file.

By default, three of these taxonomies apply to Posts and one to Links, but there’s no reason why you can’t make them also apply to another post type, or remove them from the default post type. Later on, I’ll show you how to make tags and categories apply to a new post type that you register, add them to Pages, or remove them from Posts if you don’t want to use them.

Each taxonomy has terms, which are the values you use to classify your content. So each category you add to your Posts is a term in the category taxonomy. Once you’ve registered a taxonomy, you can add terms to it via the admin screens – either by a dedicated screen for adding terms or when editing your posts.

Now you know what post types and taxonomies are, let’s see how you create a post type for your site.

Registering a Post Type

The best way to register a post type is by writing a plugin to do so. This is because if you add the post type to your theme and then switch themes at a later date, you’ll lose your post types.

Let’s create a plugin to register our post types and taxonomies for this part of the series. In your development site’s wp-content/plugins folder, create a file for your plugin and add the opening text to it as you would for any other plugin. I’m not going to show you how to do this as it should be second nature by now, but if you need to check, take a look at the source files for this series.

Next, create a function to register your post type. This needs to do three things:

  • Define the labels for the post type that your users will see when working with them in the admin screens.
  • Define the other arguments for the post type, such as whether it’s hierarchical (like a Page) or not (like a Post).
  • Register the post type using the register_post_type() function.

Your function containing all this is then hooked to the init action hook.

Here’s my function registering a project post type:

.gist table { margin-bottom: 0; }

So let’s take a look at what I’ve done in my function:

  • I’ve defined a bunch of labels based around the “Project” title for my post type.
  • I’ve used that $labels variable in the array of variables for $args to define $labels as one of those arguments.
  • I’ve used'has_archive' to ensure that there is an archive page for this post type on the site’s front end – if your post type will be embedded in other posts or pages and won’t be shown in an archive, set this to false.
  • I’ve set 'public' to true so people can see my post type, and 'hierarchical' to false as I want it to behave like a Post, not a Page.
  • I’ve defined a slug for the post type archive of projects – if I didn’t do this, the slug would be project.
  • I’ve defined the features it will support such as a title, featured image, excerpt etc.
  • I’ve specified the taxonomies it will have as category and tag. I’ll add an extra custom taxonomy to it later. If your post type will just have a custom taxonomy applied to it, that you haven’t registered yet, leave this line out.
  • Finally, I’ve used the register_post_type() function with two parameters: the id for the post type (project) and the arguments, using my $args variable.

Now if I take a look at my site, I can see my post type in the admin screens:

Project custom post type in the admin screens

Adding Featured Image Support to the Post Type

In my function to register the post type I included support for featured images but at the moment I still can’t add them to my Projects. This is because my theme doesn’t support featured images for that post type. You may remember that back in part one of this series we added theme support for post thumbnails (or featured images) to posts and pages.

Let’s fix this.

  1. In your theme, open your functions.php file and find this line:
    .gist table { margin-bottom: 0; }
  2. Edit it so it reads as follows:
    .gist table { margin-bottom: 0; }
  3. Save the file.

Now when you add a project you can upload a featured image. So now I can add post types to my site and assign categories and tags to them, with featured images and other features supported.

Registering a Taxonomy

But what if categories and tags don’t give me the level of control I need for classifying my Projects? That’s where custom taxonomies come in. You can register a custom taxonomy to apply to any post type, including Posts, Pages, Attachments or your own custom post types. Or you can register a taxonomy that applies to multiple taxonomies. Let’s register one for our new post type.

In your plugin file, add a function to register the custom taxonomy. This will include three similar elements to the posts type’s function:

  • Defining the labels
  • Defining the arguments, including labels.
  • Registering the taxonomy using register_taxonomy().

And again, the function needs to be hooked to the init action hook.

Here’s my “service” taxonomy, for a site that includes a portfolio of projects categorized by services offered:

.gist table { margin-bottom: 0; }

As with the post type, I’ve defined the labels, some arguments and then used the register_taxonomy() function to register the taxonomy. This has three parameters: the id of the taxonomy, the post type or types it applies to, and the arguments. In this case, there are multiple post types so I’ve used an array; if you just wanted to apply your taxonomy to one post type, you’d add it on its own.

Now in my admin screens I have a taxonomy I can add values to:

The service taxonomy in the admin screens

So that’s how you register a new post type and taxonomy.

Adding an Existing Taxonomy to an Existing Post Type

Applying your custom taxonomy to your chosen post type or types is easy – you do this when registering the post type. It’s also easy to apply existing taxonomies to your custom post types, as you do this when registering the post type.

But what if you want to take a taxonomy that comes with WordPress, and apply it to a default post type or to a post type that’s been added via a third party plugin?

To do this you don’t need to re-register the taxonomy or edit the code in the third party plugin. All you need to do is use the register_taxonomy_for_object_type() function (not the nicest name, huh?).

Let’s do this with categories and pages, in our existing plugin file.

  1. Open your plugin.
  2. Add this function:
    .gist table { margin-bottom: 0; }
  3. Save your plugin file.

Now when you edit a Page, you’ll see that you can assign categories to it:

Editing categories when adding a page

You aren’t limited to doing this for the “page” post type – you could add categories or tags to attachments, for example. I’ve used this technique to create a kind of featured image for each category, by uploading one image for each category and adding a custom loop to my category archives to output that image.

By default, your Pages won’t show up in your category archive pages. We’ll fix that in the next part of the series, which is about the WordPress loop.

Viewing Post Types and Taxonomies in the Site

Once you’ve registered your posts types and/or taxonomies, you need to visit Settings > Permalinks to refresh rewrite rules and make the slugs for your posts types and taxonomies work. All you need to do is visit it; you don’t need to enter anything or save changes. Although if you haven’t yet, you might take the opportunity to set up pretty permalinks.

Adding Your Post Type to the Navigation Menu

If you want to add your post type archive to the navigation menu, you’ll need to set up a custom link.

  1. Go to Appearance > Menus in the admin and click on the Links box to the left.
  2. In the URL field, type, replacing with your own domain and projects with the slug for your post type. Note that if you didn’t user the rewrite argument when registering the post type, the slug will be in the singular.
  3. In the Link text field, type Products.
  4. Click the Add to Menu button.
  5. Once the link has been added to the menu, move it to the right place and save the menu.
  6. Save your changes by clicking Save MenuDon’t miss this step!

Now if you visit your site’s front end and click on that menu link, you’ll be taken to your projects archive page. Here’s mine, with some high-profile projects added:

The projects archive

Next, add the taxonomy terms you’ve created as sub-items for that projects menu item. You can do this using the Menus admin screen or the Customizer. Once you’ve done that you’ll be able to access those in the site too. Here’s one of mine:

Service taxonomy archive page

Displaying Post Types and Taxonomies via Template Files

WordPress will use the first template file for displaying your custom post types and taxonomies that it comes across in the theme template hierarchy, which we studied in detail in part one of this series.

At the moment using our theme, it will use the archive.php file, which in turn uses the loop.php file to output the loop.

Let’s create a new template file for our post type archive. If you remember from what you learned in part one, we’ll need to call it archive-project.php. So go ahead and create a file with that name in your theme, and copy the contents of archive.php into it.

First, let’s change the title.

  1. In your archive-project.php file, find these lines:
    .gist table { margin-bottom: 0; }
  2. Delete the first of those two lines and edit the second so it reads as follows:
    .gist table { margin-bottom: 0; }
  3. Save the file.

Note that I’ve included the text inside a filter called project_archive_title. This means that I or anyone else using my theme can write a function attached to that hook at any time to change that text. If you’re ever adding static text to a template file it’s good practice to put it inside a filter.

Note: If you need a recap on filters, go back to part three of this series.

Next, let’s add the featured image to our archive page. We do this by creating a new template part for the loop.

  1. Make a copy of loop.php and call it loop-project.php.
  2. In your archive-project.php file, find this line:
    .gist table { margin-bottom: 0; }
  3. Edit it so it reads:
    .gist table { margin-bottom: 0; }
  4. Save the template file.
  5. Open your new loop file and find this line:
    .gist table { margin-bottom: 0; }
  6. Below that, add this:
    .gist table { margin-bottom: 0; }

Now let’s take a look at the archive page:

Projects archive with featured image added

Now add that featured image to your loop-single.php file, so any Post or Project with a featured image will display that on its single page. I’m not going to tell you to do this as I’d like you to use the skills you’ve learned so far instead. But if you get stuck, you can take a look at the source files for this series – you’ll need the part four version of the theme.

The layout with the featured images isn’t perfect right now so I’m going to make some tweaks to the styling for the h2 element – you can see this in the source files.

If you prefer to display an excerpt on your projects archive instead of the content, you can edit your loop-project.php file to use the_excerpt() where you’ve used the_content(). I’m sticking with the full content as I’m going to keep the content brief.

Next, let’s take a look at taxonomy term archives for our custom taxonomy. Here’s how this looks right now:

The taxonomy term archive without a featured image

To change the way the taxonomy term archive works, you need to create a new template file for your taxonomy called taxonomy-service.php. I’d like you to create and edit that file but I’m not going to give you detailed guidance as I’d like you to work it out for yourself using what you’ve learned so far.

Here’s what you need to do:

  1. Create the new file, and copy the relevant code from the existing files in your theme.
  2. Make sure the heading in the h1 tags fetches the currently queried object and echoes that (see archive.php for this code).
  3. Make sure the get_template_part() function to calls the loop-project.php file.

If you get stuck, you can check out the source files. Here’s the new version of the taxonomy term archive page:

The taxonomy term archive with featured images

Now let’s add the description to the taxonomy term archive.

Start by creating some descriptions for your services via the admin screens. Here are mine:

Descriptions for the services terms in the admin screens

Now let’s edit the taxonomy-service.php file to output those descriptions. To do this, we use the term_description() function.

  1. Open the taxonomy-service.php file and find the line with the h1 element.
  2. Below it, add this code:
    .gist table { margin-bottom: 0; }
  3. Save the file.

Now if you revisit your site and open a taxonomy term archive you’ll see the description:

Taxonomy term archives with the description added

So now you have some template files in place for your project archives, including taxonomy term archives.

Outputting Links to Post Type and Taxonomy Term Archives

WordPress has a number of functions you can use to fetch and output the terms and post types that apply to a specific post or any post type.

These have a number of uses:

  • Identifying the taxonomy terms for a given post and providing a link to their archive pages.
  • Fetching a list of taxonomy terms for use in a custom loop (which you’ll learn about in the next part of this series).
  • Identifying the current post type so you can display other posts of the same post type after the main content (which we’ll look at in the next part of the series, as it involves writing a custom query).

Let’s work with the first of those applications, and add a list of taxonomy terms for each of our posts to the loop. You need to do this in your loop-project.php file and your loop-single.php file.

  1. Open loop-project.php.
  2. After the closing </div> tag for your content section, add this:
    .gist table { margin-bottom: 0; }
  3. Open loop-single.php.
  4. Again, after the content div, add this:
    .gist table { margin-bottom: 0; }
  5. Save both files.

This uses two functions:

  • In both files, you used the_terms() to output a list of the terms the current post belongs to, with three parameters: the post ID, the preceding text, and the text between each item in the list.
  • In the loop-single.php file, you used the is_singular( 'project' ) function to check we’re in a single post of the project post type, rather than any other kind of single post.

Now take a look at your single projects or an archive page for them, and you’ll see a list of services for each project, with clickable links:

project with a list of applicable services

There isn’t much spacing between that list and the footer in the screenshot above, or between it and subsequent posts on archive pages, so you might want to add a margin to that .entry-meta class. If you’d like, you can also put it in a box to make it stand out more. I’ve added some extra styling to the source files for the series and here’s my post now:

The list of services in a blue box

Post Types and Taxonomies Take WordPress Further

Without custom post types and taxonomies, WordPress would be a pretty dull system. But with them, you can add your own content and structure your site around that, turning WordPress from a blogging platform into a CMS.

In this part of the series you’ve learned how to register your own post types and taxonomies, how to create template files for them in your theme, and how to add links to taxonomy term archives in the loop.

In the next part of this series, we’ll use the custom content we’ve created when coding custom queries and loops. You’ll learn all about how the loop works and how to create your own custom queries.

See you next time!

Related Posts


Submit a Comment

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