2026年11月12日
2026年11月12日
Custom Taxonomy Templates in WordPress: taxonomy-{taxonomy}.php
This article is a localized edition of the Japanese original. Read the Japanese original
When you create a custom taxonomy (your own classification), WordPress generates an archive page for each term (an item in the taxonomy), such as https://example.com/news-category/event/. To change how this page looks, create a taxonomy template file in your theme. This article explains how to name template files and the order in which they are chosen, how to write the template, and how to build a navigation list of terms. The content is current as of October 2026 and assumes WordPress 6.9 or later.
As an example, we attach a category-style taxonomy news_category (News Category) to a custom post type news (News).
Step 1: Register the custom taxonomy
Add this to your theme's functions.php (or the child theme's functions.php if you use one). It assumes the custom post type news is registered separately.
<?php
// functions.php
function mytheme_register_news_category(): void {
register_taxonomy(
'news_category',
array( 'news' ),
array(
'labels' => array(
'name' => 'News Categories',
'singular_name' => 'News Category',
'all_items' => 'All Categories',
'edit_item' => 'Edit Category',
'add_new_item' => 'Add New Category',
),
'public' => true,
'hierarchical' => true, // true for category style, false for tag style
'show_in_rest' => true, // show it in the block editor
'show_admin_column' => true,
'rewrite' => array( 'slug' => 'news-category' ),
)
);
}
add_action( 'init', 'mytheme_register_news_category' );
Unless show_in_rest is true, the taxonomy panel does not appear on the post edit screen in the block editor.
After registering it, open Settings → Permalinks in the admin and click "Save Changes" to rebuild the URL rules (rewrite rules). Otherwise the term archive pages may return 404.
Step 2: Learn the order in which templates are chosen
When displaying a term archive, WordPress looks for files in your theme in the following order and uses the first one it finds (the template hierarchy). The example below is for the term with slug event (ID 12) in news_category.
| Order | File name | Applies to |
|---|---|---|
| 1 | taxonomy-news_category-event.php |
This term only (by slug) |
| 2 | taxonomy-news_category-12.php |
This term only (by ID; added in WordPress 6.9) |
| 3 | taxonomy-news_category.php |
Every term in news_category |
| 4 | taxonomy.php |
All custom taxonomies |
| 5 | archive.php |
Archive pages in general |
| 6 | index.php |
Every page |
If the slug contains non-ASCII characters (for example, the Japanese イベント), WordPress 4.7 and later first look for the file name with the decoded slug, taxonomy-news_category-イベント.php. However, non-ASCII file names tend to cause problems because of character encoding differences between servers and file transfer tools, so it is safer to use an alphanumeric slug or the second form, which uses the ID.
Usually a single taxonomy-news_category.php (the third one) is enough. Add the first or second form only when you want a different design for a specific term.
In block themes (such as Twenty Twenty-Five), HTML files with the same names (for example, templates/taxonomy-news_category.html) are looked up in the same order. The PHP templates in this article are written for classic themes.
Step 3: Create the taxonomy template
Create taxonomy-news_category.php in your theme folder.
<?php
/**
* taxonomy-news_category.php
* Term archive page for News Category
*/
get_header();
$term = get_queried_object(); // The term being displayed (WP_Term)
$description = term_description(); // Description (wrapped in paragraph p tags)
?>
<main id="main" class="site-main">
<header class="page-header taxonomy-header">
<h1 class="page-title"><?php echo esc_html( $term->name ); ?></h1>
<?php if ( $description ) : ?>
<div class="term-description">
<?php echo wp_kses_post( $description ); ?>
</div>
<?php endif; ?>
<p class="term-count">
<?php echo esc_html( sprintf( '%s posts', number_format_i18n( $term->count ) ) ); ?>
</p>
</header>
<?php if ( have_posts() ) : ?>
<div class="news-grid">
<?php
while ( have_posts() ) :
the_post();
get_template_part( 'template-parts/card', 'news' );
endwhile;
?>
</div>
<?php the_posts_pagination(); ?>
<?php else : ?>
<p>There are no posts in this category yet.</p>
<?php endif; ?>
</main>
<?php
get_footer();
Key points:
get_queried_object()returns the term being displayed as aWP_Termobject. You can usename,slug,term_id,description,countand moreterm_description()returns the term description entered in the admin, wrapped in paragraph<p>tags. If the description is empty, it returns an empty stringcountis the number of published posts assigned directly to that term. In a category-style taxonomy, posts assigned only to child terms are not countedget_template_part( 'template-parts/card', 'news' )loads the theme'stemplate-parts/card-news.php. Write the post card HTML in that file (if it does not exist,template-parts/card.phpis used)- The loop on this page automatically contains the posts that have that term. You do not need to write your own
WP_Query
Step 4: Change the number of posts per page
The number of posts per page on term archives follows "Blog pages show at most" under Settings → Reading. To change it only for this taxonomy, do not use query_posts() or a new WP_Query inside the template; instead, change the main query with pre_get_posts. That way pagination also works correctly.
<?php
// functions.php: show 12 posts per page only on News Category archives
add_action(
'pre_get_posts',
function ( WP_Query $query ): void {
if ( is_admin() || ! $query->is_main_query() ) {
return;
}
if ( $query->is_tax( 'news_category' ) ) {
$query->set( 'posts_per_page', 12 );
}
}
);
Step 5: Show a navigation list of terms
To list every News Category term in a sidebar or similar, use get_terms().
<?php
// sidebar.php etc.: list of News Categories
$news_categories = get_terms(
array(
'taxonomy' => 'news_category',
'hide_empty' => true, // hide terms with no posts
'orderby' => 'name',
'order' => 'ASC',
)
);
if ( ! empty( $news_categories ) && ! is_wp_error( $news_categories ) ) : ?>
<nav class="taxonomy-nav">
<h2>Categories</h2>
<ul>
<?php foreach ( $news_categories as $cat ) : ?>
<li class="<?php echo is_tax( 'news_category', $cat->term_id ) ? 'current-term' : ''; ?>">
<a href="<?php echo esc_url( get_term_link( $cat ) ); ?>">
<?php echo esc_html( $cat->name ); ?>
<span class="count">(<?php echo (int) $cat->count; ?>)</span>
</a>
</li>
<?php endforeach; ?>
</ul>
</nav>
<?php endif; ?>
- Setting
hide_emptytofalsealso shows terms that have no posts is_tax( 'news_category', $cat->term_id )returnstruewhen the current term archive is for that term. Use it to add a marker class to the current item. The second argument takes a term ID, slug or name (or an array of them)get_term_link()returns the URL of the term archive
To show a category-style taxonomy with indented parent and child terms, wp_list_categories( array( 'taxonomy' => 'news_category', 'title_li' => '' ) ) outputs a series of <li> items with child terms nested (write the outer <ul> yourself).
Step 6: Show the terms attached to a post
In a single post (single-news.php) or a post card, show the terms attached to that post as links.
<?php
// Inside single-news.php or a card template: show the post's terms as links
$terms = get_the_terms( get_the_ID(), 'news_category' );
if ( $terms && ! is_wp_error( $terms ) ) {
$links = array();
foreach ( $terms as $t ) {
$links[] = sprintf(
'<a href="%s" class="term-link">%s</a>',
esc_url( get_term_link( $t ) ),
esc_html( $t->name )
);
}
echo '<div class="post-terms">' . implode( ' ', $links ) . '</div>';
}
get_the_terms() returns false when there are no terms and a WP_Error on error, so check both before using the result. If you only need the links in a row, the one-liner the_terms( get_the_ID(), 'news_category', '', ' ' ) also works.
Troubleshooting
| Symptom | What to check |
|---|---|
| Term archive returns 404 | Did you click "Save Changes" under Settings → Permalinks? Is public set to true? |
| Your template is not used | Does the taxonomy name in the file name (news_category) match the registered name? Is the file in the active theme (or child theme) folder? |
| No posts are shown | Are the posts published and assigned the term? Is the post type included in the second argument of register_taxonomy()? |
| No term panel on the post edit screen | Is show_in_rest set to true? |
You can check which template is being used with a plugin such as Query Monitor.
Summary
- The taxonomy template is
taxonomy-{taxonomy-name}.php. To change only a specific term, usetaxonomy-{taxonomy-name}-{slug}.php(from 6.9 you can also use the ID) - In the template, get the current term with
get_queried_object()and show its description withterm_description(). Display posts with the normal loop - Change the number of posts by modifying the main query with
pre_get_posts - List terms with
get_terms(), get URLs withget_term_link(), and detect the current term withis_tax() - After registering a taxonomy, save the permalink settings again
Related articles:
- How to create a custom taxonomy in WordPress
- Understanding the WordPress template hierarchy: which file is used
- How to create an archive page for a WordPress custom post type
Questions about WordPress? The publisher of this site is based in Japan and replies in Japanese. Contact (Japanese)