Skip to main content

Course Progress

Loading...

🏷️ Custom Taxonomies

Create custom ways to organize and categorize content

Master hierarchical and non-hierarchical taxonomies in WordPress

Learning Objectives

  • Understand WordPress taxonomies
  • Register custom taxonomies with register_taxonomy()
  • Create hierarchical (category-like) taxonomies
  • Build non-hierarchical (tag-like) taxonomies
  • Associate taxonomies with post types
  • Configure taxonomy labels and arguments
  • Query posts by taxonomy terms
  • Display taxonomies in templates

Understanding Taxonomies

Taxonomies are ways to group and organize content in WordPress. They create relationships between different pieces of content, making it easier for users to find related information.

💡
Key Concept
A taxonomy is a grouping mechanism for posts (or custom post types). Categories and tags are examples of built-in taxonomies. Custom taxonomies allow you to create your own grouping systems.

Taxonomy Components

  • Taxonomy: The classification system itself (e.g., "Categories")
  • Terms: Individual items within a taxonomy (e.g., "Technology", "Sports")
  • Term Relationships: Connections between posts and terms

Built-in vs Custom Taxonomies

Built-in Taxonomies Type Used For
category Hierarchical Posts - Main topics
post_tag Non-hierarchical Posts - Keywords
nav_menu Non-hierarchical Navigation menus
link_category Non-hierarchical Links (deprecated)
post_format Non-hierarchical Post formats

Registering Custom Taxonomies

Basic Taxonomy Registration

<?php
/**
 * Register a simple custom taxonomy
 */
function mytheme_register_product_category() {
    $args = array(
        'hierarchical' => true,
        'label'        => 'Product Categories',
        'public'       => true,
        'show_in_rest' => true,
    );
    
    register_taxonomy( 'product_category', 'product', $args );
}
add_action( 'init', 'mytheme_register_product_category' );

Complete Hierarchical Taxonomy (Category-like)

<?php
/**
 * Register hierarchical taxonomy for products
 */
function mytheme_register_product_category_taxonomy() {
    
    $labels = array(
        'name'                       => _x( 'Product Categories', 'Taxonomy General Name', 'mytheme' ),
        'singular_name'              => _x( 'Product Category', 'Taxonomy Singular Name', 'mytheme' ),
        'menu_name'                  => __( 'Categories', 'mytheme' ),
        'all_items'                  => __( 'All Categories', 'mytheme' ),
        'parent_item'                => __( 'Parent Category', 'mytheme' ),
        'parent_item_colon'          => __( 'Parent Category:', 'mytheme' ),
        'new_item_name'              => __( 'New Category Name', 'mytheme' ),
        'add_new_item'               => __( 'Add New Category', 'mytheme' ),
        'edit_item'                  => __( 'Edit Category', 'mytheme' ),
        'update_item'                => __( 'Update Category', 'mytheme' ),
        'view_item'                  => __( 'View Category', 'mytheme' ),
        'separate_items_with_commas' => __( 'Separate categories with commas', 'mytheme' ),
        'add_or_remove_items'        => __( 'Add or remove categories', 'mytheme' ),
        'choose_from_most_used'      => __( 'Choose from the most used', 'mytheme' ),
        'popular_items'              => __( 'Popular Categories', 'mytheme' ),
        'search_items'               => __( 'Search Categories', 'mytheme' ),
        'not_found'                  => __( 'Not Found', 'mytheme' ),
        'no_terms'                   => __( 'No categories', 'mytheme' ),
        'items_list'                 => __( 'Categories list', 'mytheme' ),
        'items_list_navigation'      => __( 'Categories list navigation', 'mytheme' ),
    );
    
    $args = array(
        'labels'                     => $labels,
        'hierarchical'               => true,
        'public'                     => true,
        'show_ui'                    => true,
        'show_admin_column'          => true,
        'show_in_nav_menus'          => true,
        'show_tagcloud'              => true,
        'show_in_rest'               => true,
        'rest_base'                  => 'product-categories',
        'rest_controller_class'      => 'WP_REST_Terms_Controller',
        'rewrite'                    => array(
            'slug'         => 'products/category',
            'with_front'   => false,
            'hierarchical' => true,
        ),
        'query_var'                  => true,
        'default_term'               => array(
            'name' => 'Uncategorized',
            'slug' => 'uncategorized',
        ),
    );
    
    register_taxonomy( 'product_category', array( 'product' ), $args );
}
add_action( 'init', 'mytheme_register_product_category_taxonomy', 0 );

Non-Hierarchical Taxonomy (Tag-like)

<?php
/**
 * Register non-hierarchical taxonomy for products
 */
function mytheme_register_product_tag_taxonomy() {
    
    $labels = array(
        'name'                       => _x( 'Product Tags', 'Taxonomy General Name', 'mytheme' ),
        'singular_name'              => _x( 'Product Tag', 'Taxonomy Singular Name', 'mytheme' ),
        'menu_name'                  => __( 'Tags', 'mytheme' ),
        'all_items'                  => __( 'All Tags', 'mytheme' ),
        'new_item_name'              => __( 'New Tag Name', 'mytheme' ),
        'add_new_item'               => __( 'Add New Tag', 'mytheme' ),
        'edit_item'                  => __( 'Edit Tag', 'mytheme' ),
        'update_item'                => __( 'Update Tag', 'mytheme' ),
        'view_item'                  => __( 'View Tag', 'mytheme' ),
        'separate_items_with_commas' => __( 'Separate tags with commas', 'mytheme' ),
        'add_or_remove_items'        => __( 'Add or remove tags', 'mytheme' ),
        'choose_from_most_used'      => __( 'Choose from the most used tags', 'mytheme' ),
        'popular_items'              => __( 'Popular Tags', 'mytheme' ),
        'search_items'               => __( 'Search Tags', 'mytheme' ),
        'not_found'                  => __( 'No tags found', 'mytheme' ),
        'no_terms'                   => __( 'No tags', 'mytheme' ),
        'items_list'                 => __( 'Tags list', 'mytheme' ),
        'items_list_navigation'      => __( 'Tags list navigation', 'mytheme' ),
    );
    
    $args = array(
        'labels'                     => $labels,
        'hierarchical'               => false,
        'public'                     => true,
        'show_ui'                    => true,
        'show_admin_column'          => true,
        'show_in_nav_menus'          => true,
        'show_tagcloud'              => true,
        'show_in_rest'               => true,
        'rewrite'                    => array(
            'slug'         => 'product-tag',
            'with_front'   => false,
        ),
        'update_count_callback'      => '_update_post_term_count',
        'query_var'                  => true,
        'capabilities'               => array(
            'manage_terms' => 'manage_categories',
            'edit_terms'   => 'manage_categories',
            'delete_terms' => 'manage_categories',
            'assign_terms' => 'edit_posts',
        ),
    );
    
    register_taxonomy( 'product_tag', array( 'product' ), $args );
}
add_action( 'init', 'mytheme_register_product_tag_taxonomy', 0 );

register_taxonomy() Arguments

Argument Description Default
labels Array of labels for the taxonomy array()
description Short descriptive summary ''
public Whether taxonomy is public true
publicly_queryable Whether queries can be performed value of public
hierarchical Is taxonomy hierarchical false
show_ui Generate default UI in admin value of public
show_in_menu Show in admin menu value of show_ui
show_in_nav_menus Available for selection in menus value of public
show_in_rest Include in REST API false
rest_base REST API base slug taxonomy name
show_tagcloud Available for tag cloud widget value of show_ui
show_in_quick_edit Show in quick/bulk edit panel value of show_ui
show_admin_column Display column in post lists false
meta_box_cb Callback for meta box display null
meta_box_sanitize_cb Callback for sanitizing meta box null
capabilities Array of capabilities array()
rewrite Permalink rewrite rules true
query_var Query var for this taxonomy taxonomy name
update_count_callback Callback to update term count ''
default_term Default term settings null
sort Whether to sort terms null

Associating Taxonomies with Multiple Post Types

Shared Taxonomy Across Post Types

<?php
/**
 * Register taxonomy for multiple post types
 */
function mytheme_register_shared_taxonomy() {
    
    $labels = array(
        'name'          => _x( 'Topics', 'Taxonomy General Name', 'mytheme' ),
        'singular_name' => _x( 'Topic', 'Taxonomy Singular Name', 'mytheme' ),
        'menu_name'     => __( 'Topics', 'mytheme' ),
    );
    
    $args = array(
        'labels'            => $labels,
        'hierarchical'      => true,
        'public'            => true,
        'show_admin_column' => true,
        'show_in_rest'      => true,
        'rewrite'           => array( 'slug' => 'topic' ),
    );
    
    // Associate with multiple post types
    $post_types = array( 'post', 'product', 'portfolio' );
    
    register_taxonomy( 'topic', $post_types, $args );
}
add_action( 'init', 'mytheme_register_shared_taxonomy' );

// Add existing taxonomy to additional post type
function mytheme_add_categories_to_pages() {
    register_taxonomy_for_object_type( 'category', 'page' );
    register_taxonomy_for_object_type( 'post_tag', 'page' );
}
add_action( 'init', 'mytheme_add_categories_to_pages' );

Common Taxonomy Examples

🏢

Brand

Product brands/manufacturers

🎨

Color

Product color variations

📏

Size

Product sizes (S, M, L, XL)

📍

Location

Geographic locations

🎭

Genre

Movie/book genres

💼

Department

Company departments

🏆

Skill Level

Beginner, Intermediate, Advanced

📅

Year

Year of production/release

🔧

Features

Product features/capabilities

Displaying Taxonomies in Templates

Display Terms for a Post

<?php
// Get terms for current post
$terms = get_the_terms( get_the_ID(), 'product_category' );

if ( $terms && ! is_wp_error( $terms ) ) :
    ?>
    <div class="product-categories">
        <h3><?php _e( 'Categories:', 'mytheme' ); ?></h3>
        <ul>
            <?php foreach ( $terms as $term ) : ?>
                <li>
                    <a href="<?php echo esc_url( get_term_link( $term ) ); ?>">
                        <?php echo esc_html( $term->name ); ?>
                    </a>
                    <span class="term-count">(<?php echo $term->count; ?>)</span>
                </li>
            <?php endforeach; ?>
        </ul>
    </div>
<?php endif; ?>

<?php
// Display terms as comma-separated list
$term_list = get_the_term_list( 
    get_the_ID(), 
    'product_category', 
    '<div class="categories">Categories: ', 
    ', ', 
    '</div>' 
);

if ( $term_list && ! is_wp_error( $term_list ) ) {
    echo $term_list;
}

List All Terms in a Taxonomy

<?php
// Get all terms in taxonomy
$terms = get_terms( array(
    'taxonomy'   => 'product_category',
    'hide_empty' => false,
    'orderby'    => 'name',
    'order'      => 'ASC',
    'parent'     => 0, // Only top-level terms
) );

if ( ! empty( $terms ) && ! is_wp_error( $terms ) ) :
    ?>
    <ul class="category-list">
        <?php foreach ( $terms as $term ) : ?>
            <li>
                <a href="<?php echo esc_url( get_term_link( $term ) ); ?>">
                    <?php echo esc_html( $term->name ); ?>
                </a>
                
                <?php
                // Get child terms
                $children = get_terms( array(
                    'taxonomy'   => 'product_category',
                    'hide_empty' => false,
                    'parent'     => $term->term_id,
                ) );
                
                if ( ! empty( $children ) ) :
                    ?>
                    <ul class="sub-categories">
                        <?php foreach ( $children as $child ) : ?>
                            <li>
                                <a href="<?php echo esc_url( get_term_link( $child ) ); ?>">
                                    <?php echo esc_html( $child->name ); ?>
                                </a>
                            </li>
                        <?php endforeach; ?>
                    </ul>
                <?php endif; ?>
            </li>
        <?php endforeach; ?>
    </ul>
<?php endif; ?>

Querying Posts by Taxonomy

WP_Query with Taxonomy Parameters

<?php
// Query posts from specific term
$args = array(
    'post_type' => 'product',
    'tax_query' => array(
        array(
            'taxonomy' => 'product_category',
            'field'    => 'slug',
            'terms'    => 'electronics',
        ),
    ),
);
$query = new WP_Query( $args );

// Query with multiple terms (OR relationship)
$args = array(
    'post_type' => 'product',
    'tax_query' => array(
        array(
            'taxonomy' => 'product_category',
            'field'    => 'term_id',
            'terms'    => array( 12, 15, 18 ),
            'operator' => 'IN',
        ),
    ),
);

// Query with multiple taxonomies (AND relationship)
$args = array(
    'post_type' => 'product',
    'tax_query' => array(
        'relation' => 'AND',
        array(
            'taxonomy' => 'product_category',
            'field'    => 'slug',
            'terms'    => 'electronics',
        ),
        array(
            'taxonomy' => 'product_tag',
            'field'    => 'slug',
            'terms'    => 'featured',
        ),
    ),
);

// Exclude specific terms
$args = array(
    'post_type' => 'product',
    'tax_query' => array(
        array(
            'taxonomy' => 'product_category',
            'field'    => 'term_id',
            'terms'    => array( 10, 20 ),
            'operator' => 'NOT IN',
        ),
    ),
);

// Complex tax query
$args = array(
    'post_type' => 'product',
    'tax_query' => array(
        'relation' => 'OR',
        array(
            'taxonomy' => 'product_category',
            'field'    => 'slug',
            'terms'    => array( 'electronics' ),
        ),
        array(
            'relation' => 'AND',
            array(
                'taxonomy' => 'product_tag',
                'field'    => 'slug',
                'terms'    => array( 'featured' ),
            ),
            array(
                'taxonomy' => 'product_brand',
                'field'    => 'slug',
                'terms'    => array( 'apple', 'samsung' ),
            ),
        ),
    ),
);

Useful Taxonomy Functions

Working with Terms

<?php
// Check if term exists
if ( term_exists( 'electronics', 'product_category' ) ) {
    // Term exists
}

// Get term by different fields
$term = get_term_by( 'slug', 'electronics', 'product_category' );
$term = get_term_by( 'name', 'Electronics', 'product_category' );
$term = get_term_by( 'id', 15, 'product_category' );

// Get term data
$term_id = $term->term_id;
$term_name = $term->name;
$term_slug = $term->slug;
$term_description = $term->description;
$term_count = $term->count;
$term_parent = $term->parent;

// Get term meta
$term_color = get_term_meta( $term_id, 'color', true );

// Get term link
$term_link = get_term_link( $term );

// Check if on term archive
if ( is_tax( 'product_category' ) ) {
    // On product_category archive
}

if ( is_tax( 'product_category', 'electronics' ) ) {
    // On specific term archive
}

// Get current term on archive
$current_term = get_queried_object();

// Add term to post
wp_set_object_terms( $post_id, array( 'electronics', 'computers' ), 'product_category' );

// Remove all terms from post
wp_remove_object_terms( $post_id, array( 'electronics' ), 'product_category' );

// Get term children
$children = get_term_children( $term_id, 'product_category' );

// Get term ancestors
$ancestors = get_ancestors( $term_id, 'product_category', 'taxonomy' );

// Count posts in term
$count = $term->count;

// Get posts in term
$posts = get_posts( array(
    'post_type' => 'product',
    'tax_query' => array(
        array(
            'taxonomy' => 'product_category',
            'field'    => 'term_id',
            'terms'    => $term_id,
        ),
    ),
) );

Best Practices

Taxonomy Best Practices

  • Choose appropriate structure: Hierarchical for categories, flat for tags
  • Use descriptive names: Make taxonomy purpose clear
  • Avoid conflicts: Prefix taxonomy names uniquely
  • Plan URL structure: Consider SEO when setting rewrites
  • Enable REST API: For Gutenberg block editor support
  • Set default terms: Provide fallback for uncategorized content
  • Limit depth: Don't create overly complex hierarchies
  • Consider performance: Too many taxonomies can slow queries
Taxonomy names must not exceed 32 characters and cannot contain capital letters or spaces. Always use lowercase with underscores.

Practice Exercise

💻
Create Custom Taxonomies

Build a complete taxonomy system:

  1. Register hierarchical taxonomy for products
  2. Register non-hierarchical taxonomy for tags
  3. Create shared taxonomy for multiple CPTs
  4. Add existing categories to pages
  5. Display terms in single templates
  6. Create term archive template
  7. Build term list widget
  8. Query posts by multiple taxonomies
  9. Add term meta fields
  10. Test in WordPress admin

Additional Resources