Wordpress - How to Add Custom Fields to Custom Taxonomies in Wordpress CLEANLY

You can save attachment id in options table and get to display that attachment

There are three main functions to add the meta box for taxonomy.Which are invoked through following hooks:

  1. {taxonomy_name}_add_form_fields
  2. {taxonomy_name}_edit_form_fields
  3. edited_{taxonomy_name}
  4. create_{taxonomy_name}

Here you can change the taxonomy_name with any predefined or custom taxonomy accordingly. I am using “Woocommerce product taxonomy” and created a plugin for same. Please review following functions to add custom field:

{taxonomy_name}_add_form_fields add new custom field to add new term page form.Here I am creating a field to add a Class for term.

Add the following code in functions.php in your theme

public function mj_taxonomy_add_custom_meta_field() {
            <div class="form-field">
                <label for="term_meta[class_term_meta]"><?php _e( 'Add Class', 'MJ' ); ?></label>
                <input type="text" name="term_meta[class_term_meta]" id="term_meta[class_term_meta]" value="">
                <p class="description"><?php _e( 'Enter a value for this field','MJ' ); ?></p>
    add_action( 'product_cat_add_form_fields', 'mj_taxonomy_add_custom_meta_field', 10, 2 );

{taxonomy_name}_edit_form_fields add a field on term edit page

public function mj_taxonomy_edit_custom_meta_field($term) {

        $t_id = $term->term_id;
        $term_meta = get_option( "taxonomy_$t_id" ); 
        <tr class="form-field">
        <th scope="row" valign="top"><label for="term_meta[class_term_meta]"><?php _e( 'Add Class', 'MJ' ); ?></label></th>
                <input type="text" name="term_meta[class_term_meta]" id="term_meta[class_term_meta]" value="<?php echo esc_attr( $term_meta['class_term_meta'] ) ? esc_attr( $term_meta['class_term_meta'] ) : ''; ?>">
                <p class="description"><?php _e( 'Enter a value for this field','MJ' ); ?></p>

add_action( 'product_cat_edit_form_fields','mj_taxonomy_edit_custom_meta_field', 10, 2 );

public function mj_save_taxonomy_custom_meta_field( $term_id ) {
        if ( isset( $_POST['term_meta'] ) ) {

            $t_id = $term_id;
            $term_meta = get_option( "taxonomy_$t_id" );
            $cat_keys = array_keys( $_POST['term_meta'] );
            foreach ( $cat_keys as $key ) {
                if ( isset ( $_POST['term_meta'][$key] ) ) {
                    $term_meta[$key] = $_POST['term_meta'][$key];
            // Save the option array.
            update_option( "taxonomy_$t_id", $term_meta );

add_action( 'edited_product_cat', 'mj_save_taxonomy_custom_meta_field', 10, 2 );  
add_action( 'create_product_cat', 'mj_save_taxonomy_custom_meta_field', 10, 2 );

Option 2 is the cleanest method - which I've also used a number of times. Unfortunately, there is no default term_metadata table in WordPress yet. This post also covers the same approach, http://shibashake.com/wordpress-theme/add-term-or-taxonomy-meta-data

And of course, there's a plugin for that too :) http://wordpress.org/extend/plugins/taxonomy-metadata/

1) Default wp_options table

I really don't understand why folks propose

update_option( "taxonomy_term_$t_id", $term_meta );

when we can have one single option, whose indexes are the Term ID and the custom fields as values

$options = get_option( 'taxonomy_term_meta' );
$options[$t_id] = $term_meta;
update_option( 'taxonomy_term_meta', $options );

and then simply pull the option and get the value stored for a given Term ID

$options = get_option( 'taxonomy_term_meta' );
echo $options[$tax->term_id];

2) Custom wp_taxonomymeta table

That's what the plugin Taxonomy Metadata, linked by James, does. And it's quite simple, once this table is created, the functions add_, get_, update_ and delete_metadata will start working with 'taxonomy'. Like so:

function add_term_meta($term_id, $meta_key, $meta_value, $unique = false) {
    return add_metadata('taxonomy', $term_id, $meta_key, $meta_value, $unique);

function delete_term_meta($term_id, $meta_key, $meta_value = '') {
    return delete_metadata('taxonomy', $term_id, $meta_key, $meta_value);

function get_term_meta($term_id, $key, $single = false) {
    return get_metadata('taxonomy', $term_id, $key, $single);

function update_term_meta($term_id, $meta_key, $meta_value, $prev_value = '') {
    return update_metadata('taxonomy', $term_id, $meta_key, $meta_value, $prev_value);

3) Helper Post Type

As described in Matthew Boynes answer (I think someone mentions this too in the ticket #10142.

4) Outside the Box

In the plugin code, there's a link to the Core Ticket #10142 discussing all this. It's a 4 years old ticket, (closed)(maybelater), lots of developers jumped in, but no conclusion was reached.

By the end of it, we have this nugget (my emphasis):

paco pepe
As I need this regularly, I've wrote my own solution: Misuses the "description" field as storage container for a serialized array. The only thing that was a little tricky was to display the plain description in the admin UI. Anyway this would be the solution I was hoping for: Simply convert the description field instead of adding a bunch of new fields. If there's a need for it, then we will see the use cases after we got it and move for a searchable solution with a later version.

Pretty clever.