Menu Search
Jump to the content X X
Smashing Conf Barcelona

You know, we use ad-blockers as well. We gotta keep those servers running though. Did you know that we publish useful books and run friendly conferences — crafted for pros like yourself? E.g. our upcoming SmashingConf Barcelona, dedicated to smart front-end techniques and design patterns.

How To Create A Custom Taxonomy In WordPress

WordPress 3 introduced the custom taxonomy as a core feature. The following release of 3.1 included many features to enhance support of custom taxonomies. Better import and export handling, advanced queries with tax_query, hierarchical support, body classes and a bunch of wonderful functions to play with were all part of the package.

Let’s take an in-depth look at how to create your own custom taxonomies in WordPress, including a few advanced development examples that you can begin using in your WordPress themes and plugins today.

Further Reading on SmashingMag: Link

The Custom Taxonomy In WordPress Link

WordPress’ custom taxonomies make it possible to structure large amounts of content in a logical, well-organized way. In WordPress, categories are set up as a hierarchal taxonomy, and tags are set up as a multifaceted taxonomy.

Taxonomy content can be displayed in a theme using taxonomy templates5. Within a template, there are ample ways to display your data with built-in taxonomy functions6.

custom taxonomy wordpress

Built-In Taxonomies Link

WordPress offers four built-in taxonomies out of the box:

  1. Categories (hierarchal),
  2. Tags (multifaceted),
  3. Links (multifaceted),
  4. Navigation menu (hierarchal).

Custom Taxonomies Link

WordPress provides a new method of grouping content by allowing you to create your own custom taxonomies. The core developers have created the register_taxonomy() function to handle the heavy lifting for us. All you have to do is understand how to configure all of the settings to suit your needs.

A Practical Example: Content By Location Link

A business that operates in multiple locations could benefit from organizing its content by location to allow visitors to browse news in their locality. A large news organization could organize its content by world region (Africa, Asia, Europe, Latin America, Middle East, US & Canada), as the BBC7 does in its “World” section.

How the BBC uses a location taxonomy

Create a Custom Taxonomy Link

In WordPress, you can create (or “register”) a new taxonomy by using the register_taxonomy() function. Each taxonomy option is documented in detail in the WordPress Codex8.

WordPress custom taxonomy: Posts by location

 * Add custom taxonomies
 * Additional custom taxonomies can be defined here
function add_custom_taxonomies() {
  // Add new "Locations" taxonomy to Posts
  register_taxonomy('location', 'post', array(
    // Hierarchical taxonomy (like categories)
    'hierarchical' => true,
    // This array of options controls the labels displayed in the WordPress Admin UI
    'labels' => array(
      'name' => _x( 'Locations', 'taxonomy general name' ),
      'singular_name' => _x( 'Location', 'taxonomy singular name' ),
      'search_items' =>  __( 'Search Locations' ),
      'all_items' => __( 'All Locations' ),
      'parent_item' => __( 'Parent Location' ),
      'parent_item_colon' => __( 'Parent Location:' ),
      'edit_item' => __( 'Edit Location' ),
      'update_item' => __( 'Update Location' ),
      'add_new_item' => __( 'Add New Location' ),
      'new_item_name' => __( 'New Location Name' ),
      'menu_name' => __( 'Locations' ),
    // Control the slugs used for this taxonomy
    'rewrite' => array(
      'slug' => 'locations', // This controls the base slug that will display before each term
      'with_front' => false, // Don't display the category base before "/locations/"
      'hierarchical' => true // This will allow URL's like "/locations/boston/cambridge/"
add_action( 'init', 'add_custom_taxonomies', 0 );

After adding this to your theme’s functions.php file, you should see a new taxonomy under the “Posts” menu in the admin sidebar. It works just like categories but is separate and independent.

WordPress custom taxonomy: Posts by location

After adding a few terms to your new taxonomy, you can begin to organize the content in your posts by location. A new “Locations” box will appear to the right of your posts in the WordPress admin area. Use this the way you would categories.

Let’s use this “location” taxonomy as a jumping-off point to learn more about working with taxonomy functions and content.

Create a Taxonomy Template for Your Theme Link

Taxonomies: Bringing order to chaos in WordPress

When you add a custom taxonomy to a WordPress theme, you can display its content using one of WordPress’ taxonomy theme templates9.

  • taxonomy-{taxonomy}-{slug}.php
    We could use this to create a theme template for a particular location, such as taxonomy-location-boston.php for the term “boston.”
  • taxonomy-{taxonomy}.php
    If the taxonomy were location, WordPress would look for taxonomy-location.php.
  • taxonomy.php
    This template is used for all custom taxonomies.
  • archive.php
    If no taxonomy-specific template is found, then the taxonomy that lists pages will use the archive template.
  • index.php
    If no other template is found, then this will be used.

Let’s use taxonomy-location.php to display our content. The template file could look something like this:

 * Locations taxonomy archive
$term = get_term_by( 'slug', get_query_var( 'term' ), get_query_var( 'taxonomy' ) );
<div class="wrapper">
  <div class="primary-content">
    <h1 class="archive-title"><?php echo apply_filters( 'the_title', $term->name ); ?> News</h1>

    <?php if ( !empty( $term->description ) ): ?>
    <div class="archive-description">
      <?php echo esc_html($term->description); ?>
    <?php endif; ?>

    <?php if ( have_posts() ): while ( have_posts() ): the_post(); ?>

    <div id="post-<?php the_ID(); ?>" <?php post_class('post clearfix'); ?>>
      <h2 class="post-title"><a href="<?php the_permalink(); ?>" rel="bookmark"><?php the_title(); ?></a></h2>
      <div class="content clearfix">
        <div class="post-info">
          <p><?php the_time(get_option('date_format')); ?> by <?php the_author_posts_link(); ?></p>
        </div><!--// end .post-info -->
        <div class="entry">
          <?php the_content( __('Full story…') ); ?>
    </div><!--// end #post-XX -->

    <?php endwhile; ?>

    <div class="navigation clearfix">
      <div class="alignleft"><?php next_posts_link('« Previous Entries') ?></div>
      <div class="alignright"><?php previous_posts_link('Next Entries »') ?></div>

    <?php else: ?>

    <h2 class="post-title">No News in <?php echo apply_filters( 'the_title', $term->name ); ?></h2>
    <div class="content clearfix">
      <div class="entry">
        <p>It seems there isn't anything happening in <strong><?php echo apply_filters( 'the_title', $term->name ); ?></strong> right now. Check back later, something is bound to happen soon.</p>

    <?php endif; ?>
  </div><!--// end .primary-content -->

  <div class="secondary-content">
    <?php get_sidebar(); ?>
  </div><!--// end .secondary-content -->

<?php get_footer(); ?>

(Normally, we would load a template part for the loop10, but for the sake of simplicity, I’ve skipped that step. As an alternative to get_term_by(), we could use single_term_title()11 and term_description()12 on this taxonomy archive template to display or retrieve the title and description of the taxonomy term.)

In this example, we’ve used a function called get_term_by() to retrieve all of the data associated with a taxonomy term in the form of an object. The object returned by the get_term_by()13 function contains the following details about the term:

  • ID
  • name
  • slug
  • group
  • taxonomy
  • taxonomy ID
  • description
    If you need to know the latest news in Boston, then look no further.
  • parent
    0 (or the ID)
  • count
    1 (i.e. the number of posts with this term selected)

We’ve used this object, then, to display information about the current term name and description in the taxonomy-location.php template.

Using Taxonomy Conditionals Link

Conditional tags can be used in WordPress to determine what content is displayed on a particular page depending on the conditions met by the page. Taxonomy templates have their own set of conditionals:

  • is_tax()
    When any taxonomy archive page is being displayed.
  • is_tax( 'location' )
    When a taxonomy archive page for the “location” taxonomy is being displayed.
  • is_tax( 'location', 'boston')
    When the archive page for the “location” taxonomy with the slug of “boston” is being displayed.
  • is_tax( 'location', array( 'boston', 'new-york', 'philadelphia' ) )
    Returns true when the “location” taxonomy archive being displayed has a slug of either “boston,” “new-york” or “philadelphia.”
  • taxonomy_exists()
    When a particular taxonomy is registered via register_taxonomy().

Working With Taxonomy Functions Link

Many functions for working with taxonomies are available in WordPress. Let’s go through a few commons examples of how to use them in practice.

Display a List of Taxonomy Terms Link

Most navigation systems begin with an unordered list. You can generate an unordered list of links to taxonomy archive pages using the wp_list_categories() function. This function is very customizable14 and can handle most of the scenarios that you’ll encounter as a theme developer.

 * Create an unordered list of links to active location archives
$locations_list = wp_list_categories( array(
  'taxonomy' => 'location',
  'orderby' => 'name',
  'show_count' => 0,
  'pad_counts' => 0,
  'hierarchical' => 1,
  'echo' => 0,
  'title_li' => 'Locations'
) );

// Make sure there are terms with articles
if ( $locations_list )
  echo '<ul class="locations-list">' . $locations_list . '</ul>';

If you encounter a situation that requires a custom structure, I would recommend exploring the Walker class15 or the wp_get_object_terms() function.

Create a Taxonomy Tag Cloud Link

A tag cloud provides a great way for users to browse content. The wp_tag_cloud() function makes creating a tag cloud with a custom taxonomy easy.

WordPress taxonomy term cloud example

Let’s use it to display a tag cloud of our location terms:

// Locations tag cloud
$locations_cloud = wp_tag_cloud( array(
  'taxonomy' => 'location',
  'echo' => 0
) );

// Make sure there are terms with articles
if ( $locations_cloud ): ?>
<h2>News by Location</h2>
<div class="locations-cloud">
  <?php echo $locations_cloud; ?>
<?php endif; ?>

Get All Terms in a Taxonomy Link

You will often need to work with a full list of terms in a taxonomy, and the get_terms() function can be quite handy for this. Let’s use it to show off the number of locations to which we’re providing news:

// Get a list of all terms in a taxonomy
$terms = get_terms( "location", array(
  'hide_empty' => 0,
) );
$locations = array();
if ( count($terms) > 0 ):
  foreach ( $terms as $term )
    $locations[] = $term->name;

  $locations_str = implode(', ', $locations);
<h2>Nationwide Coverage</h2>
<p>We cover stories around the country in places like <?php echo $locations_str; ?> and more. If we're not the best source for the latest news in your area, let us know!</p>
<?php endif; ?>

This will output the following HTML:

<h2>Nationwide Coverage</h2>
<p>We cover stories around the country in places like Boston, London, New York, San Francisco and more. If we're not the best source for the latest news in your area, let us know!</p>

This simple approach may not show it, but the get_terms()16 function is incredibly powerful. It allows you to get terms from multiple taxonomies at once by passing an array that contains the names of your taxonomies as the first parameter.

Working With WP_Query and tax_query Link

The WP_Query class17 enables you to create a custom loop. WordPress 3.1 introduced a new parameter for the class called tax_query18, which allows you to display content from a taxonomy in many unique ways.

Let’s use it to create a list of the most recent news posts in Boston.

 * Display a list of the most recent news in Boston
 * @class WP_Query
$locations_query = new WP_Query( array(
  'post_type' => 'post',
  'posts_per_page' => 10,
  'tax_query' => array(
      'taxonomy' => 'location',
      'field' => 'slug',
      'terms' => 'boston'
) );
// Display the custom loop
if ( $locations_query->have_posts() ): ?>
<h2>Latest News in Boston</h2>
<ul class="postlist">
  <?php while ( $locations_query->have_posts() ) : $locations_query->the_post(); ?>
  <li><span class="date"><?php the_time(get_option('date_format')); ?></span> – <a href="<?php the_permalink(); ?>" rel="bookmark"><?php the_title(); ?></a></li>
  <?php endwhile; wp_reset_postdata(); ?>
</ul><!--// end .postlist -->
<?php endif; ?>

We could easily make this set-up dynamic by using the get_term_by() or get_terms() functions that we discussed earlier.

Attaching Additional Data to a Taxonomy Link

Each taxonomy term has specific data associated with it. Out of the box, WordPress allows you to store the following information for each taxonomy term:

  • Name,
  • Slug,
  • Parent,
  • Description.

But what if you need to store more information, such as an image for the taxonomy term or a title and description for search engines, or maybe even attach the term to a particular author the way a traditional news column does? Since WordPress 2.9, developers have been able to attach additional meta data to posts, pages, custom post types, comments and users using the add_metadata(), update_metadata() and get_metadata() functions. But this does not include taxonomies such as tags and categories.

With the help of the Taxonomy Metadata2119 plugin, we can attach meta data to taxonomy terms for both built-in and custom taxonomies. This enables us to create additional taxonomy fields that will be stored in a new taxonomymeta database table.

Note to Multisite developers: I’ve encountered issues using the Taxonomy Metadata plugin on a WordPress Multisite installation. Activating the plugin network-wide results in data not being saved. Instead, activate the plugin individually for each website, and it will work properly.

(Knowing the story behind this technique and what it might mean for future WordPress upgrades is important. Currently, there is a debate on the WordPress Trac project20 about the best method for this. The method I’ll show you is one suggested by various WordPress core developers. But I strongly urge you to review the Trac project and the Codex. A standard approach could very well be built into WordPress in the future, which would therefore be more practical than what I am about to show you.)

The prerequisite to all of the examples below is to install and activate the Taxonomy Metadata2119 plugin.

Add Search Engine Title and Description Fields to Categories and Tags Link

We will use action hooks to gracefully attach additional fields to our taxonomies without editing WordPress’ core. If you’ve made it this far, then you probably have a working knowledge of WordPress filters and actions. To learn about working with hooks, I highly suggest Daniel Pataki’s article22 on the subject.

WordPress taxonomy meta data: Search engine title and description fields added to categories and tags

Let’s start by adding a text input and a textarea field to the “Add New” and “Edit” term pages on the WordPress admin screen23. We do this by placing the following functions in our theme or plugin.

The taxonomy_metadata_add() function attaches the fields to the /wp-admin/edit-tags.php?taxonomy=%taxonomy% page.
The %taxonomy% item in the URL above will change depending on the term you are editing.

 * Add additional fields to the taxonomy add view
 * e.g. /wp-admin/edit-tags.php?taxonomy=category
function taxonomy_metadata_add( $tag ) {
  // Only allow users with capability to publish content
  if ( current_user_can( 'publish_posts' ) ): ?>
  <div class="form-field">
    <label for="meta_title"><?php _e('Search Engine Title'); ?></label>
    <input name="meta_title" id="meta_title" type="text" value="" size="40" />
    <p class="description"><?php _e('Title display in search engines is limited to 70 chars'); ?></p>

  <div class="form-field">
    <label for="meta_description"><?php _e('Search Engine Description'); ?></label>
    <textarea name="meta_description" id="meta_description" rows="5" cols="40"></textarea>
    <p class="description"><?php _e('The meta description will be limited to 156 chars by search engines.'); ?></p>
  <?php endif;

The taxonomy_metadata_edit() function attaches the fields to the /wp-admin/edit-tags.php?action=edit&taxonomy=%taxonomy%&tag_ID=%id%&post_type=%post_type% page.
The %taxonomy%, %id% and %post_type% items in the URL above will change depending on the term you are editing.

We’ll use the get_metadata function here to display any saved data that exists in the form.

 * Add additional fields to the taxonomy edit view
 * e.g. /wp-admin/edit-tags.php?action=edit&taxonomy=category&tag_ID=27&post_type=post
function taxonomy_metadata_edit( $tag ) {
  // Only allow users with capability to publish content
  if ( current_user_can( 'publish_posts' ) ): ?>
  <tr class="form-field">
    <th scope="row" valign="top">
      <label for="meta_title"><?php _e('Search Engine Title'); ?></label>
      <input name="meta_title" id="meta_title" type="text" value="<?php echo get_term_meta($tag->term_id, 'meta_title', true); ?>" size="40" />
      <p class="description"><?php _e('Title display in search engines is limited to 70 chars'); ?></p>

  <tr class="form-field">
    <th scope="row" valign="top">
      <label for="meta_description"><?php _e('Search Engine Description'); ?></label>
      <textarea name="meta_description" id="meta_description" rows="5" cols="40"><?php echo get_term_meta($tag->term_id, 'meta_description', true); ?></textarea>
      <p class="description"><?php _e('Title display in search engines is limited to 70 chars'); ?></p>
  <?php endif;

These two functions control the output of the form fields. I’ve used HTML that follows WordPress’ UI patterns and styles guidelines24 for the admin area.

Saving the form’s data to the taxonomymeta database table
Now that we’ve added the form fields, we’ll need to process and save the data with the update_term_meta function that is provided by the plugin.

 * Save taxonomy metadata
 * Currently the Taxonomy Metadata plugin is needed to add a few features to the WordPress core
 * that allow us to store this information into a new database table
function save_taxonomy_metadata( $term_id ) {
  if ( isset($_POST['meta_title']) )
    update_term_meta( $term_id, 'meta_title', esc_attr($_POST['meta_title']) );

  if ( isset($_POST['meta_description']) )
    update_term_meta( $term_id, 'meta_description', esc_attr($_POST['meta_description']) );

Add the new taxonomy fields
Now that everything is in place, we’ll use action hooks to load our new functions in all the right places. By hooking the following function into the admin_init action, we ensure that it runs only on the admin side of WordPress. First, we need to make sure that the functions added by the Taxonomy Metadata plugin are available. Next, we use the get_taxonomies() function to attach the new taxonomy fields to every public taxonomy, including the built-in tags and categories.

 * Add additional taxonomy fields to all public taxonomies
function taxonomy_metadata_init() {
  // Require the Taxonomy Metadata plugin
  if( !function_exists('update_term_meta') || !function_exists('get_term_meta') ) return false;

  // Get a list of all public custom taxonomies
  $taxonomies = get_taxonomies( array(
    'public'   => true,
    '_builtin' => true
  ), 'names', 'and');

  // Attach additional fields onto all custom, public taxonomies
  if ( $taxonomies ) {
    foreach ( $taxonomies  as $taxonomy ) {
      // Add fields to "add" and "edit" term pages
      add_action("{$taxonomy}_add_form_fields", 'taxonomy_metadata_add', 10, 1);
      add_action("{$taxonomy}_edit_form_fields", 'taxonomy_metadata_edit', 10, 1);
      // Process and save the data
      add_action("created_{$taxonomy}", 'save_taxonomy_metadata', 10, 1);
      add_action("edited_{$taxonomy}", 'save_taxonomy_metadata', 10, 1);
add_action('admin_init', 'taxonomy_metadata_init');

That’s it. We’re done!

You should now see two additional fields in your tags, categories and public custom taxonomies. As mentioned at the beginning of this section, the technique can be used to handle many different scenarios. This basic framework for storing and retrieving information associated with a taxonomy should have you well on your way to mastering the management of taxonomy content.

In Conclusion Link

I hope you better understand how to organize WordPress content with the help of taxonomies. Whether hierarchal or multifaceted, a well-implemented taxonomy will simplify the way content is organized and displayed on a website. WordPress has all of the tools you need to create custom taxonomies and to group your content in new and exciting ways. How you use them is up to you!

Additional Resources Link


Footnotes Link

  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  8. 8
  9. 9
  10. 10
  11. 11
  12. 12
  13. 13
  14. 14
  15. 15
  16. 16
  17. 17
  18. 18
  19. 19
  20. 20
  21. 21
  22. 22
  23. 23
  24. 24
  25. 25
  26. 26
  27. 27
  28. 28
  29. 29
  30. 30

↑ Back to top Tweet itShare on Facebook

Kevin Leary is a Web Developer in Boston, Massachusetts. He specializes in customized WordPress setups for businesses and is an Interaction Designer at OpenView Venture Partners.

  1. 1

    Does it annoy any one else how programmers try to cram more and more html inside the programming code? Soon WordPress themes will just be one giant functions file and a stylesheet.

    • 2

      I agree. This will probably be written off as an uncritical fan-boy post and thumbed-down, but I will just mention that Drupal 7 will do most of this out-of-the-box, or by installing a module or two. No need to code.

      WordPress has its place in the world, and is superior in terms of usability out-of-the-box with for example WYSIWYG and user-friendly interface, but for more complex web sites I would choose Drupal.

      I think it’s about time Smashing Magazine seriously considered creating a Drupal section.

      • 3

        Yawn – I’m going to write that off as an uncritical fan-boy post and thumb down

      • 4

        if you have to install a node or two, that isn’t really doing “most of this” out of the box then is it?

        • 5

          With the default install profile, Drupal allows for easy taxonomy creation out of the box – and by easy, I mean through simple forms rather than php code monkeying. Drupal also has a far clearer separation of logic & markup, which Alan above craves for wordpress.

          • 6

            All true. Drupal does taxonomies out of the box, and in version 7. also Custom Content Types without a module / plugin.

            It is also an absolute dog to theme.

            WordPress’ capabilities improve all the time. This stuff isn’t really hard to integrate, but I’d like to see custom post types and taxonomies in core at some point (version 4?). If that happens, Drupal looks a lot less appealing.

          • 7

            Not really tried Drupal but I have to say, keeping the code in the functions.php file on WordPress certainly limits the possibility of clients changing settings by accident.

          • 8

            This sort of thing shouldn’t be configured via a form or by the end-user.

            Taxonomies and Custom Post Types are underlying structures. You generally won’t expose them directly to the end-user (author of the site) except for trivial cases. Instead, you use them as structures to organize your data, then expose a UI to the user that isn’t total insanity.

            Making an admin page to create taxonomies by having a dozen form fields on it to fill out is just silly. A person who is wanting to create a website shouldn’t need to bother with that stuff. They want functionality that does what they need, not to deal with low-level structures in the system to create the functionality they need.

            Taxonomies and Custom Post Types should ideally be implemented by plugins, with plugin authors writing the code for them. The author of the blog should merely pick which plugins to use for their needs, not write code.

    • 9

      Patrick Samphire

      January 5, 2012 3:31 am

      Arguably, none of this would be in the functions file at all. As taxonomies are likely to be theme-independent, the functionality should be in a plugin. Likewise, a lot of the html can be put in template files. But for the sake of a comprehensible tutorial, I think it’s okay to have it all in the functions file. It’s not like it won’t work or won’t be secure done this way.

      • 10

        Indeed, though many of the WP development team keep advising not to modify a theme function file, and instead either create a separate functions file or a plugin so as website functionality remains independently of any theme.

        • 11

          I realize it’s not ideal to print from within a function as much as I have in this article. For the sake of providing a solid demonstration, it makes things much easier as Patrick noted.

          • 12

            No, there is not a short code included with the plguin that will show the full content, but there are definitely some free plguins that will add it one for you.You could also look up my tutorial on making query_posts short codes.

      • 13

        I agree it shouldn’t be placed in themes file, but for just an example, its easier to understand.

    • 14

      Working mostly with high level MVC frameworks then jumping into WP code feels a bit like time travelling, back to the 90’s.

      Bottom line, if you are a purist programmer who loves solid architecture, design patterns and standardized coding conventions then WP will frustrate you to death.

      If you don’t care much for all this nerdy computer science stuff and just want to get the job done then you would probably love WP, if only for 2 reasons: tons of free code and themes and a very large active community.

  2. 15

    I’m actually quite interested in WP Taxonomies, and am working on a project that uses them extensively. One of the problems I’ve run into is that WP doesn’t allow different taxonomies with the same slug.
    For example, the project I’m working on is about academic musicians. Some people are just academics, some just musicians, and some both. I have two taxonomies: one writer, and the other artiste. The authors of a post are mentioned as writers (since these are translations of old writings, not recent that live authors are writing, and hence no WP_authors in sight), and the musicians mentioned in the post are mentioned as artiste-s. Hence, any given post has at least one writer, any number of artiste-s, and any number of tags.
    Now, I’d like to include a person who is both writer and artiste. Let’s call him John Doe. I made a slug entry under writer as john-doe. When I make the same one under artiste (using the Custom Post Type UI plugin []) the slug created is john-doe2. For consistent user experience, I would like the visitor to browse site/writer/john-doe and site/artiste/john-doe for the same person.
    This is not allowed by default in WP. I had to edit the table wp_terms to drop UNIQUE constraint on slug column, but this may introduce performance issues.
    I’m still trying to find the best way to do it. Another idea is to have the person name first, i.e. site/john-doe/writer and site/john-doe/artiste; this will place the focus on the person, and his role then becomes just an aspect of him. But I don’t think we can do that with custom taxonomies, at least not without significant hit in performance to order links like that.

    • 16

      Janos Janecki

      January 4, 2012 9:44 pm

      By the way, the same issue with tags/categories slugs. On my site I’m using prefix “_slug”. Well, that’s not so critical, but you asked a good question.

  3. 17

    Janos Janecki

    January 4, 2012 9:38 pm

    Another permalink question. How to keep single post’s permalink the same as taxonomy’s? I mean, something as it presented on BBC site, mentioned above.

    When viewing a single post, that’s in taxonomy, I want to get in browser’s address bar the result:

    Thanks in advance.

  4. 18

    Have you done any work with WPML and Custom Post Types/ Taxes?

    I am having an issue (since 3.3) that doesn’t allow me to use Taxes in the URL unless I add static text, here’s my code

    add_action(‘init’, ‘my_taxs_init’);

    function my_taxs_init() {
    if (!is_taxonomy(‘listing’)) {
    register_taxonomy( ‘listing’, array( ‘my_properties’),
    array( ‘hierarchical’ => TRUE, ‘label’ => __(‘Listing’),
    ‘public’ => TRUE, ‘show_ui’ => TRUE,
    ‘query_var’ => ‘action’,
    ‘rewrite’ => array(‘with_front’ => false, ‘hierarchical’ => TRUE) ) );
    if (!is_taxonomy(‘type’)) {
    register_taxonomy( ‘type’, array( ‘my_properties’),
    array( ‘hierarchical’ => TRUE, ‘label’ => __(‘Type’),
    ‘public’ => TRUE, ‘show_ui’ => TRUE,
    ‘query_var’ => ‘action’,
    ‘rewrite’ => array(‘with_front’ => false, ‘hierarchical’ => TRUE) ) );
    if (!is_taxonomy(‘action’)) {
    register_taxonomy( ‘action’, array( ‘my_properties’),
    array( ‘hierarchical’ => TRUE, ‘label’ => __(‘Action’),
    ‘public’ => TRUE, ‘show_ui’ => TRUE,
    ‘query_var’ => ‘action’,
    ‘rewrite’ => array(‘with_front’ => false, ‘hierarchical’ => TRUE) ) );
    if (!is_taxonomy(‘location’)) {
    register_taxonomy( ‘location’, array( ‘my_properties’),
    array( ‘hierarchical’ => TRUE, ‘label’ => __(‘Location’),
    ‘public’ => TRUE, ‘show_ui’ => TRUE,
    ‘query_var’ => ‘action’,
    ‘rewrite’ => array(‘with_front’ => false, ‘hierarchical’ => TRUE) ) );


    // ———————- Register Post Type
    add_action( ‘init’, ‘register_cpt_property’ );

    function register_cpt_property() {

    $labels = array(
    ‘name’ => _x( ‘Properties’, ‘property’ ),
    ‘singular_name’ => _x( ‘Property’, ‘property’ ),
    ‘add_new’ => _x( ‘Add New’, ‘property’ ),
    ‘add_new_item’ => _x( ‘Add New Property’, ‘property’ ),
    ‘edit_item’ => _x( ‘Edit Property’, ‘property’ ),
    ‘new_item’ => _x( ‘New Property’, ‘property’ ),
    ‘view_item’ => _x( ‘View Property’, ‘property’ ),
    ‘search_items’ => _x( ‘Search Properties’, ‘property’ ),
    ‘not_found’ => _x( ‘No properties found’, ‘property’ ),
    ‘not_found_in_trash’ => _x( ‘No properties found in Trash’, ‘property’ ),
    ‘parent_item_colon’ => _x( ‘Parent Property:’, ‘property’ ),
    ‘menu_name’ => _x( ‘Properties’, ‘property’ ),

    $args = array(
    ‘labels’ => $labels,
    ‘hierarchical’ => false,

    ‘supports’ => array( ‘title’, ‘editor’ ),
    ‘public’ => true,
    ‘show_ui’ => true,
    ‘show_in_menu’ => true,
    ‘menu_position’ => 5,

    ‘show_in_nav_menus’ => true,
    ‘publicly_queryable’ => true,
    ‘exclude_from_search’ => false,
    ‘has_archive’ => true,
    ‘query_var’ => true,
    ‘can_export’ => true,
    ‘rewrite’ => array(
    ‘slug’ => ‘i/%listing%/%type%/%action%/%location%’,
    ‘with_front’ => false,
    ‘feeds’ => true,
    ‘pages’ => true
    ‘capability_type’ => ‘post’,
    ‘supports’ => array(‘title’,’editor’ ,’post-formats’)

    register_post_type( ‘my_properties’, $args );

    // —————————— Permalinks
    // Listing Tax
    add_filter(‘post_link’, ‘listing_permalink’, 10, 3);
    add_filter(‘post_type_link’, ‘listing_permalink’, 10, 3);

    function listing_permalink($permalink, $post_id, $leavename) {
    if (strpos($permalink, ‘%listing%’) === FALSE) return $permalink;

    // Get post
    $post = get_post($post_id);
    if (!$post || ‘page’ === $post->post_type) return $permalink;

    // Get taxonomy terms
    $terms = wp_get_object_terms($post->ID, ‘listing’);
    if (!is_wp_error($terms) && !empty($terms) && is_object($terms[0])) $taxonomy_slug = $terms[0]->slug;
    else $taxonomy_slug = ‘defacto’;

    return str_replace(‘%listing%’, $taxonomy_slug, $permalink);

    // Type Tax
    add_filter(‘post_link’, ‘type_permalink’, 10, 3);
    add_filter(‘post_type_link’, ‘type_permalink’, 10, 3);

    function type_permalink($permalink, $post_id, $leavename) {
    if (strpos($permalink, ‘%type%’) === FALSE) return $permalink;

    // Get post
    $post = get_post($post_id);
    if (!$post || ‘page’ === $post->post_type) return $permalink;

    // Get taxonomy terms
    $terms = wp_get_object_terms($post->ID, ‘type’);
    if (!is_wp_error($terms) && !empty($terms) && is_object($terms[0])) $taxonomy_slug = $terms[0]->slug;
    else $taxonomy_slug = ‘defacto’;

    return str_replace(‘%type%’, $taxonomy_slug, $permalink);

    // Action Tax
    add_filter(‘post_link’, ‘action_permalink’, 10, 3);
    add_filter(‘post_type_link’, ‘action_permalink’, 10, 3);

    function action_permalink($permalink, $post_id, $leavename) {
    if (strpos($permalink, ‘%action%’) === FALSE) return $permalink;

    // Get post
    $post = get_post($post_id);
    if (!$post || ‘page’ === $post->post_type) return $permalink;

    // Get taxonomy terms
    $terms = wp_get_object_terms($post->ID, ‘action’);
    if (!is_wp_error($terms) && !empty($terms) && is_object($terms[0])) $taxonomy_slug = $terms[0]->slug;
    else $taxonomy_slug = ‘defacto’;

    return str_replace(‘%action%’, $taxonomy_slug, $permalink);


    // Location Tax
    add_filter(‘post_link’, ‘location_permalink’, 10, 3);
    add_filter(‘post_type_link’, ‘location_permalink’, 10, 3);

    function location_permalink($permalink, $post_id, $leavename) {
    if (strpos($permalink, ‘%location%’) === FALSE) return $permalink;

    // Get post
    $post = get_post($post_id);
    if (!$post || ‘page’ === $post->post_type) return $permalink;

    // Get taxonomy terms

    $terms = get_terms( ‘location’, array(
    ‘orderby’ => ‘count’,
    ‘hide_empty’ => 0
    ) );

    // $terms = wp_get_object_terms($post->ID, ‘location’);
    if (!is_wp_error($terms) && !empty($terms) && is_object($terms[0])) $taxonomy_slug = $terms[0]->slug . ‘/’ . $terms[1]->slug . ‘/’ . $terms[2]->slug ;
    else $taxonomy_slug = ‘defacto’;

    return str_replace(‘%location%’, $taxonomy_slug, $permalink);


    The issue is on this line; which is the slug for the post type

    'slug' => 'i/%listing%/%type%/%action%/%location%'

    What I would like

    'slug' => '%listing%/%type%/%action%/%location%'
    If I remove the i from the slug then everything breaks.. I would like to be able to take that I away. Hope someone can help me,

  5. 20

    Patrick Samphire

    January 5, 2012 3:36 am

    Thanks for this. I hadn’t come across the Taxonomy Metadata plugin before. I’d been trying to figure out how to do something similar, and it was giving me a headache.

  6. 22

    Good post !

    Don’t forget to add the line :
    'update_count_callback' => '_update_post_term_count',
    When you register a taxonomy, the counter will be wrong if you don’t do ! ;)

  7. 24

    The example of a location taxonomy leaves me confused. How is this different and better than using categories for locations?

    • 25

      It gives you the ability to add a new taxonomy and create a separation of duty between your built-in taxonomy (categories) and your new custom taxonomy (locations). This helps you divvy up data and leave categories to blog posts and locations to it’s specific job.

    • 26

      I would say that taxonomies as locations act more like tags, but its main role is to help you to manage and organise your content.

      Some other examples might include e.g. recipes: back in the day you may have had a category called ‘Meats’ and sub-categories called ‘Chicken’ and ‘Beef’ etc. and another category ‘Vegetables’ with the corresponding subcategories. However, you might have a recipe that doesn’t come under either category that easily but you might file it in both instead.

      Heaven forbid if you then want to then sort the recipe by cuisine type – such as Italian, Indian, Chinese, Korean etc.

      In this instance, we have a custom post type ‘recipe’ with several custom taxonomies: one called ‘Cuisine Type’, the other ‘Ingredients’, another ‘Cooking time’ etc. depending on how much flexibility you want to offer your visitor.

      It is then easy for a visitor to looking for an Italian meal that requires aubergine that takes 30 minutes to cook using various dropdowns – I recommend Scribu’s Query Multiple Taxonomies – although there are now a couple of alternatives.

      Not something a category could offer as easily ;)

      • 27

        All good points. I remember when we didn’t have custom taxonomies and you had to pseudo create taxonomies :(

      • 28

        Can you name those alternatives please?
        I have been looking for ages for a plugin that offers “live” drill-down search. As in one that auto updates available terms once a term is selected from any taxonomy, showing the remainder ones.

        For example if one selects Cuisine Type ->Italian, then hide on-the-spot any terms from the Ingredients taxonomy that don’t belong to an Italian recipe.

        Does anyone know of a plugin that does this?

  8. 29

    I’m pretty sure Navigation Menus are not a taxonomy, but a post type stored in wp_posts as nav_menu_item. Links are stored in wp_links as their own object type, which also includes various table columns for storing meta data.

  9. 30

    What are you thinking? Its a blogging platform silly.

    Try Joomla or Drupal if you seek advanced functionality. You will go much much farther.

    • 31

      WP is def more of a true CMS than ever, but I have to agree with you. It’s a shame you’re getting so many dislikes, though. You have a pretty valid point, especially when Drupal is in mind.

    • 32

      I’ve come to the conclusion that this tired debate lies in the eyes of the beholder. If you spend your time specializing in WordPress, then naturally you will have a much better understanding of working with it in advanced ways.

      Likewise, if you spend your time working with Joomla or Drupal then you will have a better understanding of how to customize a website using that.

      In my opinion, it comes down to the skill of the developer. If you provided a mockup to the best Drupal developers and the best WordPress developers I guarantee you would end up with two amazing content management systems.

  10. 33

    how do you check if a post (or any custom post type) is in a taxonomy, like you have in_category() with categories, do you have a in_tax (not is_tax).
    let’s say i’m in a wordpress loop and i want to check if a certain post is in tax, can i do it?


    Oshik Ernst

  11. 35

    Muzammil Hussain

    January 5, 2012 9:06 am

    Amazing tutorial. Thank you. Mostly i use wordpress and i prefer taxonomies by the way.

  12. 36

    You have to code just to get custom taxonomies in WP? I’ll def be playing around with this as I learn WP more, but I’m starting to think the belief that Drupal has the steepest learning curve may be a bit off-center. I almost feel more like Drupal spoils you.

  13. 37

    Stephen Harris

    January 6, 2012 3:31 am

    I hope WP makes meta-data for taxonomies part of the core soon. There’s a lot of existing API that could be extended to include taxonomies. As good as the Taxonomy Metadata plug-in is, it’s not feasible for plug-in development: your plug-in would be dependent on a third-party plug-in and the myriad of issues that throws up. An alternative (though perhaps only suitable for small amounts of metadata), is to follow this post by Bainternet and save the data in the options table. Not great, but I’ve seen plug-ins create a whole table with just two columns for storing their custom taxonomy metadata.

  14. 40

    Nice descriptive tutorial.Thanks!

  15. 41

    I am needing to update one of my sites, and this information is very relevant and timely. Just to put it out there, maybe someone has a suggestion?

    I am responsible for a site that is a Scientific Journal that consists of two main Custom Post Types, Issues and Articles. The complications come in because the Issues are their own objects, having cover images, lists of editors, and other metadata associated with them. Yet what is an Issue, other than a collection of individual Articles, each with its own set of metadata?

    I originally built this right after Custom Post Types made it into WP, so it was even harder to wrap my head around, and I wound up with some sort of Frankenstein Monster: Issues being both a Custom Post Type and a Custom Taxonomy, which, needless to say, is a little confusing.

    With this plugin and example, I am thinking maybe having Issues being the top level of a hierarchical taxonomy, with the individual issue being under that, eg Issues > Issue 1. Each Article would be tagged with this structure. To display an “Issue” I would then create an Archive, filtering to only articles tagged by that specific Article, but also showing the additional metadata that we attached to the specific Issue(?)

    Does that seem like the best approach for a project like this?

    Also, how would you add a field to choose an image from the Media Library this way? I use WPAlchemy to do this type of thing normally, but am not sure if it will integrate into this method easily?

    Any advice is appreciated. (besides, surely helping a fellow human learn is more rewarding than dragging out the whole WP vs Drupal thing again, right?)

  16. 42

    Thanks for this very clear article.

    I wonder how you can use a taxonomy metadata outside a taxonomy page.

    For example, if you have a picture of Boston set as a taxonomy field, it’s easy to include in the Boston news page. But how can you include it in the list of cities ?

  17. 43

    Hameed Rahamathullah

    January 16, 2012 10:23 pm

    Truly awesome tutorial post for creating custom Taxonomies. Gonna try in my next WordPress based project.

  18. 44

    Benjamin Ellis

    January 30, 2012 11:07 am

    While this is all well and good, it is probably worth thinking long and hard before creating custom taxonomies. Can what you need be achieved with heirarchical categories instead? 99 times out of a hundred it can, and doing it that way saves problems with the many WordPress plugins that don’t deal well with custom taxonomies – thus saving you, or your client, a bunch of unnecessary trouble. Think twice, code once.

  19. 45

    Really useful! I am really thankful you have explained it easily.

  20. 46

    Hi Kevin

    Great article mate! I do have one question. I have tried to implement your code on a site to implement additional metadata for page title and meta description.

    The problem I am facing is that the two extra fields we create are not visible on custom taxonomies.
    Using your code to create the location taxonomy, the two extra fields are not visible when we are in the add new location part of the interface, but they are visible when we look at the standard WordPress categories and tags admin interface area.

    Any idea how we can expose the two fields inside custom taxonomy admin areas?



    • 47

      Hey Kevin
      Been looking into this.
      In this part of the code you provided…

      // Get a list of all public custom taxonomies
      $taxonomies = get_taxonomies( array(
      ‘public’ => true,
      ‘_builtin’ => true
      ), ‘names’, ‘and’);

      If we change ” ‘_builtin’ => true ” to ” ‘_builtin’ => false “, then the opposite happens, the meta data field boxes are visible in our custom taxonomies, but not the standard WordPress Category and Tags admin panels.

      Removing the above code and replacing it with:
      ” $taxonomies=get_taxonomies(); ”

      This seems to make the meta data field boxes available on ALL taxonomies.

      What are your thoughts? Correct fix?

      How do we display these fields thou in our header.php file?


  21. 48

    One other thing Kevin…

    How do we display these two values in our header.php file?



  22. 49

    Excellent article as always!

    I have a question related:

    In my new project i have movies custom post type and i want to create the genres taxonomy, if i create the taxonomy not hierarchical i have:

    Genres (taxonomy)
    Comedy (term)
    Action (term)
    Scifi (term)

    With this method in WordPress menu i can’t select Genre taxonomy as father of all genres for making an expandable menu with all genres listed…

    Is better to make the genres taxonomy hierarchical and create a genres term:

    Genres (taxonomy)
    Genres (term)
    ->Comedy (term child)
    ->Action (term child)
    ->Scifi (term child)

    Or this is an incorrect way to do?

  23. 50


    I was able to create the custom taxonomy for locations and I can add lcoations in admin and in posts. I also created the taxonomy-location.php page inside the theme folder but when I input the URL I get a 404 error. Am I missing something?

    I’m just starting out with WP so I appreciate your assistance.

    • 51

      I’m having exactly the same problem. I’ve implemented this with a Custom Post Type and its working but adding a taxonomy to Posts just fails to find anything. All I’ve done is add the code to add locations, added a term to locations in a post and saved it. I can acess the post via categories as normal but not by the new taxonomy. I also have a taxonomy.php template. Is there anything else that needs to be defined?

      • 52

        Found the problem. The permalinks need to be refreshed by going into Settings->Permalinks changing them, then change back!

  24. 53

    Thanks for the sweet tutorial. One question: how can I display all the posts in a custom taxonomy?

    The code you provided relies on the settings under Settings > Reading > ‘Blog pages show at most,’ which I currently have set to 5 posts, but I have more than 5 posts in my custom post type. I would like them all to be displayed on one page. How can I modify taxonomy.php to display all the posts?

    Thanks for your help.

  25. 54

    Vajrasar Goswami

    May 24, 2013 3:29 am

    This is one great writeup on the subject. I am glad I read it full. Kudos.

    I have one question though, what you meant when you said following in the ‘Working with WP_Query and Tax_Query’ ‘s example section?

    “We could easily make this set-up *dynamic* by using the get_term_by() or get_terms() functions that we discussed earlier.”

  26. 55

    Luca Bonaldo

    June 7, 2013 2:23 am

    if the functions do not exist get_term_meta update_term_meta and what can I do to solve the problem..
    In my plugin trick that comes out error:
    Fatal error: Call to undefined function update_term_meta() in /membri/bonny85/wp-content/plugins/lb-events-calendar/lb-events-calendar-taxonomy.php on line 85

  27. 56

    Hi, very helpful post. Thanks.

  28. 57

    After frustating with the code, I found this plugin
    Hope it will help other visitor like me who stupid in code.
    BTW, thanks :)

  29. 58

    Any idea, how can I show which post has which “Location” on edit.php (Admin panel >> All posts)? Just like categories

  30. 59

    Hello Sir,

    First of all i am deeply thankful for your wonderful guideline on above taxonomy ..

    Sir, can you please help me for solution :

    I am using your all above step to create my own custom taxonomy , all is fine .. but i need to exclude 2-3 category form above loop ( thats photos, video category , 1,2 resp ID ),

    I am not using any custom post type , just using default post, category .. to above loop … but above loop include all category post , but i need to exclude photos and video category from its .. hence my page will be showing all post except photos and video post(category)..

    can you please help me.. i am using wordpress 3.8.1

  31. 60

    Fantastic. I’ve been trying to get dynamic custom post type terms to work from the wordpress menu for 8 hours. Thank you so much for this tutorial, I must have looked at 100 websites today.

    This is my solution for anyone which it helps
    $term= at the top relates to the tax query which displays the CURRENT term

    1. In a file taxonomy-yourtaxonomyname.php add

    (This allows you to have a separate file for each post type)

    2. In a file called loop-yourtaxfile.php add the following
    (This gives you a list of posts from the current term and pagination)
    ‘posts_per_page’ => 9,
    ‘paged’ => $paged,
    ‘tax_query’ => array(
    ‘taxonomy’ => ‘yourtaxonomyname’,
    ‘field’ => ‘slug’,
    ‘terms’ => $term
    ) );
    // Display the custom loop
    if ( $my_query->have_posts() ): ?>

    have_posts() ) : $my_query->the_post(); ?>

    <div id="post-” >

    //post content

    I added the custom post type to the wordpress nav menu and then used the “automatically add all decendents as submenu items” plugin so I have a drop down menu of the terms. When a term is selected it now loads a page with just those terms on it.

  32. 61

    Unfortunately in my post above it’s stripping out all the php even if I put a gap in <? php – sorry! Hopefully people get the idea, I thought it may help some

  33. 62

    Thank you so much, i know this is an oldie, but still saved my sanity.

  34. 63

    Adam Driggers

    April 27, 2014 11:16 pm

    This is very useful, thank you.

    I was trying to use your example to get a un ordered list for my custom category. It worked, but the links that it produced didn’t work, any idea how to fix?

  35. 64

    Great post thanks, although I’m not seeing my additional fields coming up in my custom taxonomies, only in the “category” taxonomy. Any help appreciated, I’m stumped.

  36. 65

    Nice article and I think a good start to sorting out my content issue. Do you have additional advise or a direction as to how to combine 2 domains of similar/same content. My company (USA) acquired a distributor located in the UK. I quickly modified their site to fit our brand but now I would like to combine both into 1 WP site. Most content is the same EXCEPT they spell and phrase some things differently. Not sure how to sort and display shared content as well as content specific to each country and what can be construed as duplicate content if 2 pages exist with only a few words swapped out. Any suggestions?


↑ Back to top