Introduction to WordPress term meta

As we gear up for the release of WordPress version 4.4, many developers are planning some cool features that they’ve been waiting for years to implement. These features revolve around term metadata.

The original Trac ticket is over 6 years old. A potential roadmap was outlined by Andrew Nacin a little over 2 years ago on the changes that would need to happen to get term meta in core WordPress. Even if/when those changes happened, it didn’t guarantee the inclusion of term meta.

Fortunately, this long sought-after developer feature was green-lighted for WordPress 4.4.

And, it’s awesome.

What is term meta?

Terms are individual objects within a taxonomy. For example, the category taxonomy can have many categories (i.e., terms).

Meta (short for “metadata”) is simply additional data that can be tied to an object. This data can pretty much be anything.

Term meta, therefore, is additional data about specific taxonomy terms.

WordPress has long allowed for meta on other types of objects, such as:

  • Posts
  • Comments
  • Users

If you’ve used WordPress, you’ve used metadata in some way, even if you didn’t know it. Metadata is actually pretty important because it allows plugins (and even core itself) to add extra data to the various objects that’s not accounted for in the main table.

A good example of this is featured images in core. Core stores the image ID as metadata because there’s no field for the image ID in the posts table. So, metadata to the rescue!

Example uses of term meta

This is just an extremely small sampling of ideas:

  • Term images.
  • Storing the color hex code (e.g., #000000) to assign a color.
  • Storing the document title via an SEO plugin.
  • Category templates that can be re-used (like page templates).
  • Attach an icon to a term.
  • Theme sidebar position.
  • Privatize posts that are in a specific category in a membership plugin.

One of my favorite quotes about term meta was written by John James Jacoby.

Without term-meta, it’s impossible to describe a tag or category beyond it’s literal description. Terms are crippled without metadata, and are infinitely powerful with it.

I couldn’t have said it better myself.

Terms are not posts

Before moving forward, I want to touch on something that’s been at the heart of the discussion of whether term meta even belongs in core at all.

Terms are not posts.

If you’re building terms with vast amounts of metadata, particularly things that are maybe even handled better by posts, you might need to reconsider the architecture of your plugin. Sure, the API allows for easy relationships, but if that’s the only reason you’re using a taxonomy instead of a custom post type, I’d urge you to consider the possibility of just building a post-to-post relationship (not that hard).

How to use term meta

If you’re a developer and have ever used post, comment, or user meta, you’re pretty much already familiar with the foundations of term meta. The biggest things you need to know are the new meta wrapper functions for terms:

  • add_term_meta()
  • update_term_meta()
  • delete_term_meta()
  • get_term_meta()

They work just like their *_post|comment|user_meta() siblings. I don’t think it’s worth covering these in great detail.

Instead, I’m going to walk you through building a simple plugin using term meta. What this plugin will do is allow users to assign a color to a category. This can be useful for all sorts of things. One thing that immediately comes to mind is designing a news theme and having a specific color associated with each category, which is typical of many news publications.

Registering meta

The first step is to register our meta with the register_meta() function. We’ll name our custom meta key color.

add_action( 'init', 'jt_register_meta' );

function jt_register_meta() {

    register_meta( 'term', 'color', 'jt_sanitize_hex' );

Note that we added a sanitize callback function for when meta is saved. So, let’s go ahead and add the code for that. What the function is going to do is simply make sure that we have a valid color hex code.

function jt_sanitize_hex( $color ) {

    $color = ltrim( $color, '#' );

    return preg_match( '/([A-Fa-f0-9]{3}){1,2}$/', $color ) ? $color : '';

Getting term meta

In order to get the stored term color, we use the get_term_meta() function like so:

$color = get_term_meta( $term_id, 'color', true );

For the purposes of the plugin we’re building, let’s create a wrapper function that can return the color with or without the preceding # mark.

function jt_get_term_color( $term_id, $hash = false ) {

    $color = get_term_meta( $term_id, 'color', true );
    $color = jt_sanitize_hex( $color );

    return $hash && $color ? "#{$color}" : $color;

Adding form fields

Category color field

Up to this point, everything we’ve done is not much different than post meta, for example. Now, things will get a bit different.

If you’ve never added custom form fields to one of the taxonomy admin screens, there are two hooks you need to know about:

  • {$taxonomy}_add_form_fields – Hook for adding fields to the “new term” form.
  • {$taxonomy}_edit_form_fields – Hook for adding fields to the “edit term” form.

There’s actually several hooks available if you dig into wp-admin/edit-tags.php and wp-admin/edit-tag-form.php. I highly encourage scanning those files for calls to do_action() and finding the most appropriate hook for your use case.

One of the biggest things to note is that the add new term and edit term forms have different HTML markup. This means that you need two separate callback functions for handling output of the fields.

For the add new form, we’ll use the following code:

add_action( 'category_add_form_fields', 'ccp_new_term_color_field' );

function ccp_new_term_color_field() {

    wp_nonce_field( basename( __FILE__ ), 'jt_term_color_nonce' ); ?>

    <div class="form-field jt-term-color-wrap">
        <label for="jt-term-color"><?php _e( 'Color', 'jt' ); ?></label>
        <input type="text" name="jt_term_color" id="jt-term-color" value="" class="jt-color-field" data-default-color="#ffffff" />
<?php }

For the edit form, we’ll use this code:

add_action( 'category_edit_form_fields', 'ccp_edit_term_color_field' );

function ccp_edit_term_color_field( $term ) {

    $default = '#ffffff';
    $color   = jt_get_term_color( $term->term_id, true );

    if ( ! $color )
        $color = $default; ?>

    <tr class="form-field jt-term-color-wrap">
        <th scope="row"><label for="jt-term-color"><?php _e( 'Color', 'jt' ); ?></label></th>
            <?php wp_nonce_field( basename( __FILE__ ), 'jt_term_color_nonce' ); ?>
            <input type="text" name="jt_term_color" id="jt-term-color" value="<?php echo esc_attr( $color ); ?>" class="jt-color-field" data-default-color="<?php echo esc_attr( $default ); ?>" />
<?php }

What we’ve done is add a basic text input to both forms for the color. Remember, we’re only adding this for the category taxonomy. So, take note that the category part of the hooks is related specifically to that taxonomy.

Saving term meta

In order to save the fields we added, we need to hook into create_{$taxonomy} (new term form) and edit_{$taxonomy} (edit term form). Fortunately, we can use the same callback function with this and just check that our field was posted.

Again, take note that the category part refers specifically to the name of the taxonomy.

add_action( 'edit_category',   'jt_save_term_color' );
add_action( 'create_category', 'jt_save_term_color' );

function jt_save_term_color( $term_id ) {

    if ( ! isset( $_POST['jt_term_color_nonce'] ) || ! wp_verify_nonce( $_POST['jt_term_color_nonce'], basename( __FILE__ ) ) )

    $old_color = jt_get_term_color( $term_id );
    $new_color = isset( $_POST['jt_term_color'] ) ? jt_sanitize_hex( $_POST['jt_term_color'] ) : '';

    if ( $old_color && '' === $new_color )
        delete_term_meta( $term_id, 'color' );

    else if ( $old_color !== $new_color )
        update_term_meta( $term_id, 'color', $new_color );

That’s pretty much it for handling the adding of custom form fields and saving the data. We’ll add a color picker in just a bit to make it look a bit prettier.

Adding a term meta column

Category color column

On the taxonomy management page, you might want to add a custom column to output your metadata. One thing to keep in mind is that the list table for taxonomies is kind of small, so too many columns can get unruly.

The first step is to let WordPress know about our custom column:

add_filter( 'manage_edit-category_columns', 'jt_edit_term_columns' );

function jt_edit_term_columns( $columns ) {

    $columns['color'] = __( 'Color', 'jt' );

    return $columns;

Then, we need to handle the output for the column:

add_filter( 'manage_category_custom_column', 'jt_manage_term_custom_column', 10, 3 );

function jt_manage_term_custom_column( $out, $column, $term_id ) {

    if ( 'color' === $column ) {

        $color = jt_get_term_color( $term_id, true );

        if ( ! $color )
            $color = '#ffffff';

        $out = sprintf( '<span class="color-block" style="background:%s;">&nbsp;</span>', esc_attr( $color ) );

    return $out;

Making things look pretty

We need a color picker instead of forcing users to manually type hex codes. And, we need to make our color column nicer.

For that, we’ll enqueue the wp-color-picker script and style while adding a little bit of custom JavaScript and CSS.

add_action( 'admin_enqueue_scripts', 'jt_admin_enqueue_scripts' );

function jt_admin_enqueue_scripts( $hook_suffix ) {

    if ( 'edit-tags.php' !== $hook_suffix || 'category' !== get_current_screen()->taxonomy )

    wp_enqueue_style( 'wp-color-picker' );
    wp_enqueue_script( 'wp-color-picker' );

    add_action( 'admin_head',   'jt_term_colors_print_styles' );
    add_action( 'admin_footer', 'jt_term_colors_print_scripts' );

function jt_term_colors_print_styles() { ?>

    <style type="text/css">
        .column-color { width: 50px; }
        .column-color .color-block { display: inline-block; width: 28px; height: 28px; border: 1px solid #ddd; }
<?php }

function jt_term_colors_print_scripts() { ?>

    <script type="text/javascript">
        jQuery( document ).ready( function( $ ) {
            $( '.jt-color-field' ).wpColorPicker();
        } );
<?php }

Only the beginning

This tutorial is just a small sampling of using the new term meta feature in WordPress 4.4. There are other developer-features related to this that I encourage you to dig into.

Feel free to post any tutorial ideas in the comments or just share what you’d like to do with the new feature.


  1. This is great. Term meta and the tutorial!

    I’d really like to see term image plugin. Is there a similar script like wp-color-picker for loading images from the media library? Or standard way of doing it?

    1. You can use the wordpress thickbox library for doing so:

    2. I’ve been doing this for the last few years in my Unique Headers plugin …

      It originally used a plugin to add the taxonomy term support, but I designed it to switch over to use the built in system if you use it with WordPress version 4.4 or newer.

      Feel free to rip the functionality out of my plugin and use it in your own stuff 🙂

    3. My Custom Header Extended plugin loads the media modal. So, you have at least 3 different examples to use. 🙂

    4. I just read this post right now… I recently developed a jQuery plugin “wp-media-picker” that works similar like “wp-color-picker” – yes, a little late, but maybe it still helps 🙂

  2. yes, it’s great. Waiting for WordPress 4.4 alive.

  3. My clients mostly ask to be able to order terms.
    I usually use a plugin for that, but would this be a good case of use (“order” as term meta)?

    1. If you have been using another plugin, you may like to switch to this new one by Ronald Huereca. It uses the new built-in system in WordPress and is somewhat more efficient than the other plugins I’ve seen for this.

      1. In reply to Ryan Hellyer

        Thanks! I ll give it a try.

      1. In reply to JJJ

        +1000 for WP Term Order. It is great and customers love it.

  4. Great write-up as always.

    I made this base class a while ago to help spin-up speedy term-meta interfaces:

    Y’all feel free to rip it apart if need be.

    1. Awesome! I might just be borrowing some ideas from that.

  5. Piet

    Hi Justin, please consider me slow on the uptake; I simply don’t understand the purpose of these term meta data. Take your detailed example, now you have great colors of categories in the backend! Is there any functional use for these in the frontend? I fail to see it and would appreciate if you can shine a light on that for me.

    1. Check out top of the tutorial. There is get_term_meta() function or wrapper function jt_get_term_color() for getting color in front end.

    2. Here’s a screenshot of something you can do with it. This is GitHub’s labels system. Each of the label have an associated color.

  6. Pablo Cruz

    Hello, Justin. Sorry if this has already been addressed but: Will it need a new DB table for terms meta? Thanks!

    1. Yes, there’ll be a new table for term meta.

  7. John Farrar

    What about related post types, say we had a post type for orders and a post type for order items. Relationships like that, we of course should not store the main post type style data in a post collection.

    Yet, I am wondering if the term meta could be used to use a one to many relationship index or a many to one index relationship. What doesn’t seem pragmatic is a many to many relationship using term data. This is one of the most common related data not yet part of WordPress.

    Could you share your thoughts on how to deal with those three relationships. Especially it would be great if we could figure out how to do this as friendly as we do with the new term meta!

  8. brody

    can the form be displayed on the front end using a short code?

    1. You can build a front-end form for pretty much anything. So, yes.

  9. Wooow !

    That’s exactly what i need 😉 that’s a great news, can’t wait to test it !

    thanks a lot 🙂

  10. Nice tutorial. This will be a great resource for people as WordPress 4.4 was just released today. Term meta opens a lot of opportunity for developers. I’m gonna to integrate it with my Meta Box plugin. So exciting!

  11. Woow,

    Just integrate your solution in a project and it works just fine.
    This feature will really change the way we consider WP development !

    Thanks again !


  12. Jason King

    Really helpful guidance, and I’ve put it to use already. This feature is so new, there aren’t many good tutorials yet.

    If I was going to use taxonomy meta to enable images to be associated with categories, how would I use a form control to upload an image to the field instead of using a color picker? Any thoughts welcome!

  13. Great guide! I love the new term meta it’s so much more intuitive then saving taxonomy values in the options table.

    One quick note though…instead of using both the edit_category and create_category action hooks you can just use the edited_category.

    – AJ

  14. Joel

    Thanks man, super helpful.

  15. Uugic

    Thanks for the awesome tutorial. I’m sorry. I’m not good developer. How can i fix it Notice: Undefined variable: term_id in

    1. Uugic

      Please delete this comment. i found solution. Thanks again for theme amazing very useful tutorial.

  16. Harvey

    Hi Justin,

    Can the taxonomies themselves (post categories, custom ones etc) also have arbitrary metadata assigned [*_taxonomy_meta()] or is does this apply just to their terms?

    1. No, you can’t have taxonomy meta. Only objects can have meta. In this case, the object is the term. It’s just like posts and comments (objects) can have meta but post types and comment types can’t.

      1. Harvey In reply to Justin Tadlock

        Ok, that makes sense, thanks. I suppose that post/comment types could be regarded as metadata in themselves for the creation of posts & comments, just as OOP classes are for instanciating objects. I’m not sure if this inorthogonality re. metadata makes sense for taxonomy though as these aren’t used to stamp out new terms but more as a container for grouping them as I see it.

Comments are closed.