When developing WordPress themes, we often need different single post templates for different types of content. A post in a video category may need a video-player layout, while a post in a gallery category may need an image-heavy template.
There are several ways to do that in WordPress.
The first option is to use custom templates for posts. Since WordPress 4.7, page-template style headers can also be used with posts if you specify the post type in the template header.
<?php
/*
Template Name: Full-width layout
Template Post Type: post, page, event
*/
// Template code goes below.
This is easy to implement, but it does require editors to pick the template manually when publishing a post.
Another option is to use post formats. That approach requires the theme to support post formats, and then you can load different template parts based on the format.
<?php get_template_part( 'content', get_post_format() ); ?>
That method also adds an extra decision in the editor, and the set of built-in post formats is limited.
Load single post templates based on categories
The more attractive idea is to make WordPress behave more like category templates do for archives: if a post belongs to a category, automatically load a corresponding single template file. That way, the editor only assigns a category and WordPress does the rest.
The following code makes that possible. Once it is added to the theme, creating a file named single-category-slug.php lets WordPress use that file automatically. If a child category has no matching template, WordPress can fall back to a parent category template. If there is no category-specific single template at all, WordPress uses the normal default single template.
add_filter( 'single_template', function ( $template ) {
foreach ( (array) get_the_category() as $cat ) {
if ( file_exists( TEMPLATEPATH . "/single-category-{$cat->slug}.php" ) ) {
return TEMPLATEPATH . "/single-category-{$cat->slug}.php";
}
if ( $cat->category_parent ) {
$parent = get_category( $cat->category_parent );
if ( file_exists( TEMPLATEPATH . "/single-category-{$parent->slug}.php" ) ) {
return TEMPLATEPATH . "/single-category-{$parent->slug}.php";
}
}
}
return $template;
} );
Use Yoast SEO’s primary category feature to control category-based templates
After using the simple version for a while, one problem becomes obvious: if a post belongs to more than one category, WordPress just uses whichever category appears first. That is not always the category we want to control the template.
Yoast SEO includes a primary category feature, which makes it possible to be more explicit. The adjusted version below checks the Yoast primary category first and tries to load the single template that matches that category.
add_filter( 'single_template', function ( $template ) {
// Get the primary category.
$primary_cat_id = get_post_meta( get_the_ID(), '_yoast_wpseo_primary_category', true );
foreach ( (array) get_the_category() as $cat ) {
// Prefer the primary category template.
if ( $primary_cat_id && (int) $primary_cat_id === (int) $cat->term_id ) {
$primary_template = TEMPLATEPATH . "/single-category-{$cat->slug}.php";
if ( file_exists( $primary_template ) ) {
return $primary_template;
}
}
// Fall back to a normal category-specific single template.
if ( file_exists( TEMPLATEPATH . "/single-category-{$cat->slug}.php" ) ) {
return TEMPLATEPATH . "/single-category-{$cat->slug}.php";
}
}
return $template;
} );
Yoast SEO automatically sets the first category as the primary category if the user does not change it, and in many cases editors do not assign multiple categories to the same post anyway. So the primary-category adjustment is usually small but valuable.
This approach creates a nice balance: the theme can switch templates automatically based on category, while the editor does not have to perform extra template-selection steps. There is still room to refine the idea further, especially if you want it to cooperate with post formats or custom post templates and define a clear priority order between those systems.
