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 a WP_Term object. You can use name, slug, term_id, description, count and more
  • term_description() returns the term description entered in the admin, wrapped in paragraph <p> tags. If the description is empty, it returns an empty string
  • count is the number of published posts assigned directly to that term. In a category-style taxonomy, posts assigned only to child terms are not counted
  • get_template_part( 'template-parts/card', 'news' ) loads the theme's template-parts/card-news.php. Write the post card HTML in that file (if it does not exist, template-parts/card.php is 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_empty to false also shows terms that have no posts
  • is_tax( 'news_category', $cat->term_id ) returns true when 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, use taxonomy-{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 with term_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 with get_term_link(), and detect the current term with is_tax()
  • After registering a taxonomy, save the permalink settings again

Related articles:

Questions about WordPress? The publisher of this site is based in Japan and replies in Japanese. Contact (Japanese)

お気軽にご相談ください

お見積りへ お問い合わせへ
▲