23.2 Defining Custom Taxonomies in Configuration
Alright, let’s get our hands dirty. You’ve outgrown the default ‘category’ and ‘post_tag’. Good for you. They’re fine for a quick blog, but for a serious site—be it a portfolio, a product catalog, or a repository of weird mushroom facts—you need custom taxonomies. This is where you stop letting WordPress dictate your content structure and start building it yourself.
Think of a taxonomy as a way to group things. ‘Category’ is a taxonomy. ‘Tag’ is a taxonomy. We’re just making new ones. The real magic trick here is that we’re going to define these in our theme’s functions.php file (or better yet, in a site-specific plugin) using the register_taxonomy() function. This function is your new best friend; it’s powerful, but a bit fussy about its arguments.
The register_taxonomy() Blueprint
Here’s the basic skeleton. I’ll throw the code at you and then we’ll dissect it like a high school biology class.
function my_brilliant_taxonomies() {
$args = array(
'labels' => $labels, // We'll do this in a second
'public' => true,
'hierarchical' => false, // True for category-like, false for tag-like
'show_in_rest' => true, // Crucial for the Gutenberg editor
'rewrite' => array( 'slug' => 'genre' ),
);
register_taxonomy( 'genre', 'book', $args );
}
add_action( 'init', 'my_brilliant_taxonomies' );
Let’s break it down. The register_taxonomy() function takes three key parameters:
'genre': The name of your taxonomy. Keep it lowercase, one word, no spaces. This is its internal slug.'book': The post type(s) you want to attach this taxonomy to. Pass a string for one, or an array likearray('book', 'post')for multiple.$args: A big array of settings that defines its personality. This is where 90% of the configuration happens.
Crafting the Perfect $args Array
The $args array is where you move from “it works” to “it works exactly how I want.” Let’s flesh out that skeleton with some muscle.
function my_brilliant_taxonomies() {
// First, we create the labels. This controls the text you see in the admin UI.
$labels = array(
'name' => _x( 'Genres', 'taxonomy general name', 'textdomain' ),
'singular_name' => _x( 'Genre', 'taxonomy singular name', 'textdomain' ),
'search_items' => __( 'Search Genres', 'textdomain' ),
'all_items' => __( 'All Genres', 'textdomain' ),
'edit_item' => __( 'Edit Genre', 'textdomain' ),
'update_item' => __( 'Update Genre', 'textdomain' ),
'add_new_item' => __( 'Add New Genre', 'textdomain' ),
'new_item_name' => __( 'New Genre Name', 'textdomain' ),
'menu_name' => __( 'Genre', 'textdomain' ),
);
$args = array(
'labels' => $labels,
'public' => true, // Makes it visible on the front-end and admin
'hierarchical' => true, // Now it behaves like Categories (has parents/children)
'show_ui' => true, // Generate the default admin UI for it
'show_in_rest' => true, // MUST be true to show in the Block Editor (Gutenberg)
'show_admin_column' => true, // Show a column for it in the post type's list table
'query_var' => true, // Allows you to query posts by this taxonomy (e.g., ?genre=scifi)
'rewrite' => array( 'slug' => 'book-genre' ), // Customize the permalink structure
);
register_taxonomy( 'genre', 'book', $args );
}
add_action( 'init', 'my_brilliant_taxonomies' );
Critical Choices and Their Consequences
hierarchical: This is the big one. Set it totrueif you need parent/child relationships (e.g., Music -> Jazz -> Bebop). Set it tofalsefor a flat structure like tags. Choose wisely; changing this later is a massive pain because it doesn’t convert existing terms. You’ll be stuck with a weird hybrid mess.show_in_rest: This isn’t just important; it’s non-negotiable if you’re using the modern Block Editor. Forget this, and your taxonomy will be a ghost in the admin—present in the backend code but utterly invisible to the person writing posts. I don’t know why this isn’ttrueby default; it’s a classic WordPress designer quirk.rewrite: This controls the URL structure for your taxonomy archives. Theslugyou set here is what will appear in the URL (e.g.,yoursite.com/book-genre/scifi). If you don’t set this, WordPress will just use the taxonomy name (genre), which is usually fine. But if you have multiple taxonomies that might conflict, or you just want prettier URLs, this is your lever.
The “Oh Crap, I Already Have Content” Pitfall
Here’s a fun reality: if you’re adding this to an existing site, you might already have a bunch of posts. The taxonomy will be created, but it will be empty. Your old categories or tags won’t magically port over. You have two options: manually reassign terms to all your content (a soul-crushing endeavor) or write a migration script. This is why planning your content structure before you start creating content is the single best piece of advice I can give you. The trenches are full of developers who learned this the hard way. Don’t be one of them.