Learning Objectives
- Understand term metadata in WordPress
- Add custom fields to taxonomy terms
- Use term meta API functions
- Create admin forms for term metadata
- Save and retrieve term meta data
- Display term metadata in templates
- Handle different field types
- Implement best practices for term meta
Understanding Term Metadata
Term metadata allows you to store additional information for taxonomy terms beyond the default name, slug, and description. Since WordPress 4.4, the term meta API provides a standardized way to add custom fields to categories, tags, and custom taxonomies.
Key Concept
Term Meta API Functions
Basic Term Meta Usage
Adding and Retrieving Term Meta
<?php
// Add term meta
$term_id = 15;
add_term_meta( $term_id, 'custom_color', '#667eea', true );
add_term_meta( $term_id, 'featured_image_id', 123, true );
add_term_meta( $term_id, 'display_order', 10, true );
// Get term meta
$color = get_term_meta( $term_id, 'custom_color', true );
$image_id = get_term_meta( $term_id, 'featured_image_id', true );
$order = get_term_meta( $term_id, 'display_order', true );
// Update term meta
update_term_meta( $term_id, 'custom_color', '#764ba2' );
update_term_meta( $term_id, 'display_order', 5 );
// Delete term meta
delete_term_meta( $term_id, 'temporary_flag' );
// Get all meta for a term
$all_meta = get_term_meta( $term_id );
// Check if meta exists
if ( metadata_exists( 'term', $term_id, 'custom_color' ) ) {
// Meta key exists
}
Adding Term Meta Fields to Admin
Complete Term Meta Implementation
<?php
/**
* Add custom fields to category admin
*/
class MyTheme_Term_Meta {
public function __construct() {
// Category meta
add_action( 'category_add_form_fields', array( $this, 'add_category_fields' ) );
add_action( 'category_edit_form_fields', array( $this, 'edit_category_fields' ) );
add_action( 'created_category', array( $this, 'save_category_fields' ) );
add_action( 'edited_category', array( $this, 'save_category_fields' ) );
// Custom taxonomy meta
add_action( 'product_category_add_form_fields', array( $this, 'add_taxonomy_fields' ) );
add_action( 'product_category_edit_form_fields', array( $this, 'edit_taxonomy_fields' ) );
add_action( 'created_product_category', array( $this, 'save_taxonomy_fields' ) );
add_action( 'edited_product_category', array( $this, 'save_taxonomy_fields' ) );
// Add admin scripts
add_action( 'admin_enqueue_scripts', array( $this, 'admin_scripts' ) );
}
/**
* Add fields to Add New Term screen
*/
public function add_taxonomy_fields() {
?>
<div class="form-field">
<label for="term_color"><?php _e( 'Color', 'mytheme' ); ?></label>
<input type="text" name="term_color" id="term_color" class="color-picker" value="">
<p class="description"><?php _e( 'Select a color for this category', 'mytheme' ); ?></p>
</div>
<div class="form-field">
<label for="term_image"><?php _e( 'Category Image', 'mytheme' ); ?></label>
<input type="hidden" name="term_image_id" id="term_image_id" value="">
<div id="term_image_wrapper"></div>
<button type="button" class="button upload_image_button"><?php _e( 'Upload/Add Image', 'mytheme' ); ?></button>
<button type="button" class="button remove_image_button" style="display:none;"><?php _e( 'Remove Image', 'mytheme' ); ?></button>
<p class="description"><?php _e( 'Featured image for this category', 'mytheme' ); ?></p>
</div>
<div class="form-field">
<label for="term_icon"><?php _e( 'Icon Class', 'mytheme' ); ?></label>
<input type="text" name="term_icon" id="term_icon" value="">
<p class="description"><?php _e( 'Enter icon class (e.g., fa-shopping-cart)', 'mytheme' ); ?></p>
</div>
<div class="form-field">
<label for="term_featured">
<input type="checkbox" name="term_featured" id="term_featured" value="1">
<?php _e( 'Featured Category', 'mytheme' ); ?>
</label>
<p class="description"><?php _e( 'Check to mark as featured', 'mytheme' ); ?></p>
</div>
<div class="form-field">
<label for="term_order"><?php _e( 'Display Order', 'mytheme' ); ?></label>
<input type="number" name="term_order" id="term_order" value="0">
<p class="description"><?php _e( 'Order for displaying (lower numbers first)', 'mytheme' ); ?></p>
</div>
<?php
}
/**
* Add fields to Edit Term screen
*/
public function edit_taxonomy_fields( $term ) {
// Get existing values
$color = get_term_meta( $term->term_id, 'term_color', true );
$image_id = get_term_meta( $term->term_id, 'term_image_id', true );
$icon = get_term_meta( $term->term_id, 'term_icon', true );
$featured = get_term_meta( $term->term_id, 'term_featured', true );
$order = get_term_meta( $term->term_id, 'term_order', true );
?>
<tr class="form-field">
<th scope="row"><label for="term_color"><?php _e( 'Color', 'mytheme' ); ?></label></th>
<td>
<input type="text" name="term_color" id="term_color" class="color-picker" value="<?php echo esc_attr( $color ); ?>">
<p class="description"><?php _e( 'Select a color for this category', 'mytheme' ); ?></p>
</td>
</tr>
<tr class="form-field">
<th scope="row"><label for="term_image"><?php _e( 'Category Image', 'mytheme' ); ?></label></th>
<td>
<input type="hidden" name="term_image_id" id="term_image_id" value="<?php echo esc_attr( $image_id ); ?>">
<div id="term_image_wrapper">
<?php if ( $image_id ) : ?>
<?php echo wp_get_attachment_image( $image_id, 'thumbnail' ); ?>
<?php endif; ?>
</div>
<button type="button" class="button upload_image_button"><?php _e( 'Upload/Add Image', 'mytheme' ); ?></button>
<button type="button" class="button remove_image_button" <?php echo $image_id ? '' : 'style="display:none;"'; ?>><?php _e( 'Remove Image', 'mytheme' ); ?></button>
<p class="description"><?php _e( 'Featured image for this category', 'mytheme' ); ?></p>
</td>
</tr>
<tr class="form-field">
<th scope="row"><label for="term_icon"><?php _e( 'Icon Class', 'mytheme' ); ?></label></th>
<td>
<input type="text" name="term_icon" id="term_icon" value="<?php echo esc_attr( $icon ); ?>">
<p class="description"><?php _e( 'Enter icon class (e.g., fa-shopping-cart)', 'mytheme' ); ?></p>
</td>
</tr>
<tr class="form-field">
<th scope="row"><label for="term_featured"><?php _e( 'Featured', 'mytheme' ); ?></label></th>
<td>
<label>
<input type="checkbox" name="term_featured" id="term_featured" value="1" <?php checked( $featured, '1' ); ?>>
<?php _e( 'Featured Category', 'mytheme' ); ?>
</label>
<p class="description"><?php _e( 'Check to mark as featured', 'mytheme' ); ?></p>
</td>
</tr>
<tr class="form-field">
<th scope="row"><label for="term_order"><?php _e( 'Display Order', 'mytheme' ); ?></label></th>
<td>
<input type="number" name="term_order" id="term_order" value="<?php echo esc_attr( $order ); ?>">
<p class="description"><?php _e( 'Order for displaying (lower numbers first)', 'mytheme' ); ?></p>
</td>
</tr>
<?php
}
/**
* Save term meta fields
*/
public function save_taxonomy_fields( $term_id ) {
// Save color
if ( isset( $_POST['term_color'] ) ) {
update_term_meta( $term_id, 'term_color', sanitize_hex_color( $_POST['term_color'] ) );
}
// Save image
if ( isset( $_POST['term_image_id'] ) ) {
update_term_meta( $term_id, 'term_image_id', absint( $_POST['term_image_id'] ) );
}
// Save icon
if ( isset( $_POST['term_icon'] ) ) {
update_term_meta( $term_id, 'term_icon', sanitize_text_field( $_POST['term_icon'] ) );
}
// Save featured
$featured = isset( $_POST['term_featured'] ) ? '1' : '0';
update_term_meta( $term_id, 'term_featured', $featured );
// Save order
if ( isset( $_POST['term_order'] ) ) {
update_term_meta( $term_id, 'term_order', intval( $_POST['term_order'] ) );
}
}
/**
* Enqueue admin scripts
*/
public function admin_scripts( $hook ) {
if ( $hook != 'edit-tags.php' && $hook != 'term.php' ) {
return;
}
// Color picker
wp_enqueue_style( 'wp-color-picker' );
wp_enqueue_script( 'wp-color-picker' );
// Media uploader
wp_enqueue_media();
// Custom script
wp_enqueue_script(
'mytheme-term-meta',
get_template_directory_uri() . '/js/term-meta.js',
array( 'jquery', 'wp-color-picker' ),
'1.0.0',
true
);
}
}
// Initialize
new MyTheme_Term_Meta();
JavaScript for Media Upload
term-meta.js
jQuery(document).ready(function($) {
// Color picker
$('.color-picker').wpColorPicker();
// Media upload
var mediaUploader;
$('.upload_image_button').on('click', function(e) {
e.preventDefault();
var button = $(this);
var imageIdInput = button.siblings('#term_image_id');
var imageWrapper = button.siblings('#term_image_wrapper');
var removeButton = button.siblings('.remove_image_button');
// If uploader exists, open it
if (mediaUploader) {
mediaUploader.open();
return;
}
// Create media uploader
mediaUploader = wp.media({
title: 'Choose Image',
button: {
text: 'Use this image'
},
multiple: false
});
// When image selected
mediaUploader.on('select', function() {
var attachment = mediaUploader.state().get('selection').first().toJSON();
imageIdInput.val(attachment.id);
imageWrapper.html('<img src="' + attachment.sizes.thumbnail.url + '">');
removeButton.show();
});
mediaUploader.open();
});
// Remove image
$('.remove_image_button').on('click', function(e) {
e.preventDefault();
var button = $(this);
var imageIdInput = button.siblings('#term_image_id');
var imageWrapper = button.siblings('#term_image_wrapper');
imageIdInput.val('');
imageWrapper.html('');
button.hide();
});
});
Displaying Term Meta in Templates
Using Term Meta in Templates
<?php
// On taxonomy archive page
if ( is_tax() ) {
$term = get_queried_object();
$term_id = $term->term_id;
// Get term meta
$color = get_term_meta( $term_id, 'term_color', true );
$image_id = get_term_meta( $term_id, 'term_image_id', true );
$icon = get_term_meta( $term_id, 'term_icon', true );
$featured = get_term_meta( $term_id, 'term_featured', true );
// Display term header with custom styling
?>
<header class="term-header" style="background-color: <?php echo esc_attr( $color ); ?>">
<?php if ( $image_id ) : ?>
<div class="term-image">
<?php echo wp_get_attachment_image( $image_id, 'large' ); ?>
</div>
<?php endif; ?>
<h1 class="term-title">
<?php if ( $icon ) : ?>
<i class="<?php echo esc_attr( $icon ); ?>"></i>
<?php endif; ?>
<?php single_term_title(); ?>
</h1>
<?php if ( $featured ) : ?>
<span class="featured-badge"><?php _e( 'Featured', 'mytheme' ); ?></span>
<?php endif; ?>
<?php if ( term_description() ) : ?>
<div class="term-description">
<?php echo term_description(); ?>
</div>
<?php endif; ?>
</header>
<?php
}
// Display category grid with images
$categories = get_terms( array(
'taxonomy' => 'product_category',
'hide_empty' => false,
'meta_key' => 'term_order',
'orderby' => 'meta_value_num',
'order' => 'ASC',
) );
if ( ! empty( $categories ) && ! is_wp_error( $categories ) ) :
?>
<div class="category-grid">
<?php foreach ( $categories as $category ) :
$cat_color = get_term_meta( $category->term_id, 'term_color', true );
$cat_image = get_term_meta( $category->term_id, 'term_image_id', true );
$cat_icon = get_term_meta( $category->term_id, 'term_icon', true );
$cat_featured = get_term_meta( $category->term_id, 'term_featured', true );
?>
<div class="category-card <?php echo $cat_featured ? 'featured' : ''; ?>">
<a href="<?php echo esc_url( get_term_link( $category ) ); ?>">
<?php if ( $cat_image ) : ?>
<div class="category-image">
<?php echo wp_get_attachment_image( $cat_image, 'medium' ); ?>
</div>
<?php elseif ( $cat_icon ) : ?>
<div class="category-icon" style="background-color: <?php echo esc_attr( $cat_color ); ?>">
<i class="<?php echo esc_attr( $cat_icon ); ?>"></i>
</div>
<?php endif; ?>
<h3 class="category-title">
<?php echo esc_html( $category->name ); ?>
</h3>
<span class="category-count">
<?php printf( _n( '%s Product', '%s Products', $category->count, 'mytheme' ), $category->count ); ?>
</span>
</a>
</div>
<?php endforeach; ?>
</div>
<?php endif; ?>
Common Field Types for Term Meta
Text Input
<input type="text" name="meta_key" value="<?php echo esc_attr( $value ); ?>">
Textarea
<textarea name="meta_key" rows="5"><?php echo esc_textarea( $value ); ?></textarea>
Select Dropdown
<select name="meta_key">
<option value="option1" <?php selected( $value, 'option1' ); ?>>Option 1</option>
<option value="option2" <?php selected( $value, 'option2' ); ?>>Option 2</option>
</select>
Checkbox
<input type="checkbox" name="meta_key" value="1" <?php checked( $value, '1' ); ?>>
Radio Buttons
<label><input type="radio" name="meta_key" value="option1" <?php checked( $value, 'option1' ); ?>> Option 1</label>
<label><input type="radio" name="meta_key" value="option2" <?php checked( $value, 'option2' ); ?>> Option 2</label>
Practical Use Cases
E-commerce Categories
- Category banners
- Promotional text
- Sale percentage
- Featured products count
Portfolio Categories
- Project type icon
- Color scheme
- Client logo
- Industry sector
Blog Categories
- Author assignment
- Category header image
- Custom CSS class
- Content guidelines
Location Taxonomies
- Map coordinates
- Address details
- Contact information
- Operating hours
Best Practices
Term Meta Best Practices
- Sanitize input: Always sanitize data before saving
- Escape output: Escape data when displaying
- Use unique keys: Prefix meta keys to avoid conflicts
- Check permissions: Verify user can edit terms
- Provide defaults: Handle missing meta gracefully
- Cache queries: Use transients for expensive operations
- Document fields: Add descriptions to help users
- Consider performance: Don't add too many fields
Use WordPress color picker and media uploader for better user experience. They provide familiar interfaces that users already know from other parts of WordPress.
Term meta is not included when exporting/importing WordPress content by default. Consider creating custom export/import functionality for important term metadata.
Practice Exercise
Implement Term Metadata System