Skip to main content

Course Progress

Loading...

WordPress Template Hierarchy

Duration: 55 minutes
Module 4: Theme Architecture

Learning Objectives

  • Understand how WordPress decides which template file to use
  • Master the complete template hierarchy from most specific to most generic
  • Learn to create custom templates for different content types
  • Implement conditional tags to control template behavior
  • Build flexible themes using the template hierarchy effectively

Introduction to Template Hierarchy

The WordPress Template Hierarchy is the system that determines which template file WordPress uses to display different types of content. Think of it as a decision tree where WordPress looks for the most specific template first, then falls back to more general templates if the specific one doesn't exist.

💡
Key Concept
WordPress starts with the most specific template possible and works its way up to more general templates, finally falling back to index.php if no other template is found. This system allows for incredible flexibility in theme design.

How Template Selection Works

When a visitor requests a page on your WordPress site, WordPress determines what type of query it is (home page, single post, category archive, etc.) and then looks for template files in a specific order.

flowchart TD A[User Requests Page] --> B{Query Type?} B -->|Home Page| C[front-page.php] B -->|Single Post| D[single-{post-type}-{slug}.php] B -->|Page| E[page-{slug}.php] B -->|Category| F[category-{slug}.php] B -->|Archive| G[archive-{post-type}.php] B -->|Search| H[search.php] B -->|404| I[404.php] C --> C1[home.php] C1 --> C2[index.php] D --> D1[single-{post-type}.php] D1 --> D2[single.php] D2 --> D3[singular.php] D3 --> D4[index.php] E --> E1[page-{id}.php] E1 --> E2[page.php] E2 --> E3[singular.php] E3 --> E4[index.php] F --> F1[category-{id}.php] F1 --> F2[category.php] F2 --> F3[archive.php] F3 --> F4[index.php] style A fill:#4caf50,color:#fff style B fill:#ff9800,color:#fff style C fill:#2196f3,color:#fff style D fill:#e91e63,color:#fff style E fill:#9c27b0,color:#fff style F fill:#00bcd4,color:#fff

Visual Template Hierarchy Map

Complete Template Hierarchy Reference

Here's the complete hierarchy for all content types, from most specific to most general:

1. Home Page

// Home Page Display
front-page.php          // Used for both static front page and blog homepage
├── home.php            // Blog posts index when static front page is set
└── index.php           // Ultimate fallback

// Note: front-page.php takes precedence over home.php
// Use conditional tags to handle both cases in front-page.php

2. Single Post

// Single Post Display
single-{post-type}-{post_name}.php  // single-product-widget.php
├── single-{post-type}.php          // single-product.php
    ├── single.php                   // All single posts
        ├── singular.php             // Fallback for single posts and pages
            └── index.php            // Ultimate fallback

// Examples:
// Post with slug "hello-world" in post type "product":
// 1. single-product-hello-world.php
// 2. single-product.php
// 3. single.php
// 4. singular.php
// 5. index.php

3. Single Page

// Page Display
custom-template.php      // Page Template selected in editor
├── page-{page_name}.php    // page-about.php
    ├── page-{id}.php       // page-42.php
        ├── page.php        // All pages
            ├── singular.php // Fallback for single posts and pages
                └── index.php

// Custom Page Template Example:
/*
Template Name: Full Width Layout
Template Post Type: page, post
*/

4. Category Archive

// Category Archive Display
category-{slug}.php      // category-news.php
├── category-{id}.php    // category-5.php
    ├── category.php     // All categories
        ├── archive.php  // All archives
            └── index.php

// Example for category "News" with ID 5:
// 1. category-news.php
// 2. category-5.php
// 3. category.php
// 4. archive.php
// 5. index.php

5. Tag Archive

// Tag Archive Display
tag-{slug}.php           // tag-wordpress.php
├── tag-{id}.php         // tag-10.php
    ├── tag.php          // All tags
        ├── archive.php  // All archives
            └── index.php

6. Custom Taxonomy Archive

// Custom Taxonomy Archive Display
taxonomy-{taxonomy}-{term}.php  // taxonomy-product_cat-electronics.php
├── taxonomy-{taxonomy}.php     // taxonomy-product_cat.php
    ├── taxonomy.php            // All custom taxonomies
        ├── archive.php         // All archives
            └── index.php

7. Author Archive

// Author Archive Display
author-{nicename}.php    // author-john.php
├── author-{id}.php      // author-3.php
    ├── author.php       // All authors
        ├── archive.php  // All archives
            └── index.php

8. Date Archive

// Date Archive Display
date.php                 // All date archives
├── archive.php          // All archives
    └── index.php

// Date archives include:
// - Yearly: example.com/2024/
// - Monthly: example.com/2024/12/
// - Daily: example.com/2024/12/25/

9. Search Results

// Search Results Display
search.php               // Search results page
└── index.php           // Ultimate fallback

10. 404 Error

// 404 Not Found Display
404.php                  // Error page
└── index.php           // Ultimate fallback

11. Attachment Pages

// Attachment Display
{MIME-type}-{subtype}.php    // image-jpeg.php
├── {subtype}.php            // jpeg.php
    ├── {MIME-type}.php      // image.php
        ├── attachment.php   // All attachments
            ├── single-attachment.php
                ├── single.php
                    ├── singular.php
                        └── index.php

// MIME type examples:
// - image (image/jpeg, image/png)
// - video (video/mp4)
// - audio (audio/mpeg)
// - application (application/pdf)

12. Embeds

// Embed Template (for oEmbed)
embed-{post-type}-{post-format}.php
├── embed-{post-type}.php
    ├── embed.php
        └── wp-includes/theme-compat/embed.php

Custom Post Type Archives

Custom post types follow a similar hierarchy pattern:

// Custom Post Type Archive
archive-{post-type}.php  // archive-product.php
├── archive.php          // All archives
    └── index.php        // Ultimate fallback

// Example: Product post type
// 1. archive-product.php
// 2. archive.php
// 3. index.php

// Register archive in post type
register_post_type('product', array(
    'public' => true,
    'has_archive' => true, // or 'products' for custom slug
    'rewrite' => array('slug' => 'products'),
    // ... other arguments
));

Conditional Tags

WordPress provides conditional tags to determine what type of page is being displayed. These are essential for controlling template behavior:

<?php
// Primary Conditional Tags
if (is_home()) {
    // Blog homepage (posts page)
}

if (is_front_page()) {
    // Site front page (home or static page)
}

if (is_single()) {
    // Any single post
    if (is_single('42')) {
        // Single post with ID 42
    }
    if (is_single('hello-world')) {
        // Single post with slug
    }
}

if (is_page()) {
    // Any single page
    if (is_page('about')) {
        // Specific page by slug
    }
    if (is_page(array('about', 'contact'))) {
        // Multiple pages
    }
}

if (is_category()) {
    // Category archive
    if (is_category('news')) {
        // Specific category
    }
}

if (is_tag()) {
    // Tag archive
}

if (is_tax()) {
    // Custom taxonomy archive
    if (is_tax('product_cat', 'electronics')) {
        // Specific taxonomy term
    }
}

if (is_author()) {
    // Author archive
    if (is_author('john')) {
        // Specific author
    }
}

if (is_date()) {
    // Date-based archive
    if (is_year()) { }
    if (is_month()) { }
    if (is_day()) { }
}

if (is_archive()) {
    // Any archive page
}

if (is_search()) {
    // Search results page
}

if (is_404()) {
    // 404 error page
}

if (is_attachment()) {
    // Attachment page
}

if (is_singular()) {
    // Any single post, page, or attachment
    if (is_singular('product')) {
        // Single custom post type
    }
}

if (is_post_type_archive()) {
    // Custom post type archive
    if (is_post_type_archive('product')) {
        // Specific post type archive
    }
}

// Useful Helper Conditionals
if (is_sticky()) {
    // Sticky post
}

if (is_paged()) {
    // Paginated page (page 2, 3, etc.)
}

if (has_post_thumbnail()) {
    // Post has featured image
}

if (comments_open()) {
    // Comments are open
}

if (is_user_logged_in()) {
    // User is logged in
}

if (is_admin()) {
    // Admin area (not for front-end checks!)
}

// Combining Conditionals
if (is_single() && !is_attachment()) {
    // Single post but not attachment
}

if (is_home() || is_archive()) {
    // Blog home or any archive
}

if (is_page() && !is_page('contact')) {
    // Any page except contact
}

Template Hierarchy Best Practices

  • Start with index.php and build up - ensure your theme works with just index.php
  • Create specific templates only when needed to avoid theme bloat
  • Use template parts (get_template_part()) to avoid code duplication
  • Name templates clearly and follow WordPress naming conventions
  • Document custom page templates with clear names and descriptions
  • Use conditional tags in parent templates instead of creating many specific templates
  • Test your templates with different content types and edge cases
  • Consider child themes when planning template structure
  • Use singular.php as a smart fallback for both posts and pages

Creating Custom Page Templates

WordPress allows you to create custom templates that users can select when creating pages:

Basic Page Template

<?php
/*
Template Name: Full Width Template
Template Post Type: page, post
*/

get_header(); ?>

<div class="full-width-container">
    <?php
    while (have_posts()) : the_post(); ?>
        
        <article id="post-<?php the_ID(); ?>" <?php post_class(); ?>>
            <header class="entry-header">
                <?php the_title('<h1 class="entry-title">', '</h1>'); ?>
            </header>
            
            <div class="entry-content">
                <?php the_content(); ?>
            </div>
        </article>
        
    <?php endwhile; ?>
</div>

<?php get_footer(); ?>

Advanced Custom Template with Options

<?php
/*
Template Name: Portfolio Gallery
Template Post Type: page
Description: A custom template for displaying portfolio items in a grid
*/

get_header(); 

// Get custom field values (if using ACF)
$columns = get_field('gallery_columns') ?: 3;
$items_per_page = get_field('items_per_page') ?: 12;
?>

<div class="portfolio-template">
    <header class="page-header">
        <?php the_title('<h1>', '</h1>'); ?>
        <?php if (has_excerpt()) : ?>
            <div class="page-description">
                <?php the_excerpt(); ?>
            </div>
        <?php endif; ?>
    </header>
    
    <?php
    // Query portfolio items
    $paged = get_query_var('paged') ? get_query_var('paged') : 1;
    $portfolio = new WP_Query(array(
        'post_type' => 'portfolio',
        'posts_per_page' => $items_per_page,
        'paged' => $paged
    ));
    
    if ($portfolio->have_posts()) : ?>
        <div class="portfolio-grid columns-<?php echo esc_attr($columns); ?>">
            <?php while ($portfolio->have_posts()) : $portfolio->the_post(); ?>
                <div class="portfolio-item">
                    <a href="<?php the_permalink(); ?>" class="portfolio-link">
                        <?php if (has_post_thumbnail()) : ?>
                            <?php the_post_thumbnail('portfolio-thumb'); ?>
                        <?php endif; ?>
                        <div class="portfolio-overlay">
                            <h3><?php the_title(); ?></h3>
                            <span class="portfolio-category">
                                <?php echo get_the_term_list(get_the_ID(), 'portfolio_category', '', ', '); ?>
                            </span>
                        </div>
                    </a>
                </div>
            <?php endwhile; ?>
        </div>
        
        <!-- Pagination -->
        <div class="pagination">
            <?php
            echo paginate_links(array(
                'total' => $portfolio->max_num_pages,
                'current' => $paged
            ));
            ?>
        </div>
        
        <?php wp_reset_postdata(); ?>
    <?php endif; ?>
</div>

<?php get_footer(); ?>

Template Parts and Organization

Use template parts to organize reusable components and keep templates clean:

<?php
// get_template_part() usage

// Basic usage
get_template_part('template-parts/content');
// Looks for: template-parts/content.php

// With suffix
get_template_part('template-parts/content', 'single');
// Looks for: 
// 1. template-parts/content-single.php
// 2. template-parts/content.php

// Dynamic suffix
get_template_part('template-parts/content', get_post_type());
// For 'product' post type, looks for:
// 1. template-parts/content-product.php
// 2. template-parts/content.php

// Passing variables to template parts (WordPress 5.5+)
$args = array(
    'title' => get_the_title(),
    'show_meta' => true,
    'layout' => 'grid'
);
get_template_part('template-parts/content', 'card', $args);

// In template-parts/content-card.php:
$title = $args['title'] ?? '';
$show_meta = $args['show_meta'] ?? false;
$layout = $args['layout'] ?? 'list';
?>

<!-- Organized theme structure -->
themes/your-theme/
├── index.php
├── style.css
├── functions.php
├── header.php
├── footer.php
├── sidebar.php
├── single.php
├── page.php
├── archive.php
├── search.php
├── 404.php
├── template-parts/
│   ├── content.php
│   ├── content-single.php
│   ├── content-page.php
│   ├── content-none.php
│   ├── content-search.php
│   ├── header/
│   │   ├── site-branding.php
│   │   └── site-navigation.php
│   └── footer/
│       ├── site-info.php
│       └── footer-widgets.php
├── page-templates/
│   ├── full-width.php
│   ├── portfolio.php
│   └── contact.php
└── assets/
    ├── css/
    ├── js/
    └── images/

Real World Example: Smart Archive Template

Creating a flexible archive.php that handles multiple content types intelligently:

<?php
/**
 * Archive Template
 * Handles all archive pages with dynamic layouts
 */

get_header(); ?>

<div class="archive-wrapper">
    <header class="archive-header">
        <?php
        // Dynamic archive title
        if (is_category()) {
            echo '<h1 class="archive-title">Category: ' . single_cat_title('', false) . '</h1>';
            if (category_description()) {
                echo '<div class="archive-description">' . category_description() . '</div>';
            }
            
        } elseif (is_tag()) {
            echo '<h1 class="archive-title">Tag: ' . single_tag_title('', false) . '</h1>';
            
        } elseif (is_author()) {
            $author = get_queried_object();
            echo '<div class="author-header">';
            echo get_avatar($author->ID, 120);
            echo '<div class="author-info">';
            echo '<h1 class="archive-title">Author: ' . $author->display_name . '</h1>';
            echo '<div class="author-bio">' . $author->description . '</div>';
            echo '</div></div>';
            
        } elseif (is_date()) {
            if (is_day()) {
                echo '<h1 class="archive-title">Daily Archives: ' . get_the_date() . '</h1>';
            } elseif (is_month()) {
                echo '<h1 class="archive-title">Monthly Archives: ' . get_the_date('F Y') . '</h1>';
            } elseif (is_year()) {
                echo '<h1 class="archive-title">Yearly Archives: ' . get_the_date('Y') . '</h1>';
            }
            
        } elseif (is_post_type_archive()) {
            echo '<h1 class="archive-title">' . post_type_archive_title('', false) . '</h1>';
            
        } elseif (is_tax()) {
            $term = get_queried_object();
            $taxonomy = get_taxonomy($term->taxonomy);
            echo '<h1 class="archive-title">' . $taxonomy->labels->singular_name . ': ' . $term->name . '</h1>';
            if ($term->description) {
                echo '<div class="archive-description">' . $term->description . '</div>';
            }
            
        } else {
            echo '<h1 class="archive-title">Archives</h1>';
        }
        
        // Results count
        global $wp_query;
        if ($wp_query->found_posts > 0) {
            echo '<p class="results-count">Found ' . $wp_query->found_posts . ' results</p>';
        }
        ?>
    </header>
    
    <?php if (have_posts()) : ?>
        
        <div class="archive-content <?php echo esc_attr(get_archive_layout_class()); ?>">
            <?php
            while (have_posts()) : the_post();
                
                // Use different templates for different post types
                $post_type = get_post_type();
                
                // Try post-type specific template first
                if (locate_template('template-parts/content-' . $post_type . '.php')) {
                    get_template_part('template-parts/content', $post_type);
                    
                // Fall back to post format
                } elseif (get_post_format()) {
                    get_template_part('template-parts/content', get_post_format());
                    
                // Default template
                } else {
                    get_template_part('template-parts/content', 'archive');
                }
                
            endwhile;
            ?>
        </div>
        
        <!-- Smart Pagination -->
        <?php
        the_posts_pagination(array(
            'mid_size' => 2,
            'prev_text' => '← Previous',
            'next_text' => 'Next →',
            'before_page_number' => '<span class="screen-reader-text">Page </span>'
        ));
        ?>
        
    <?php else : ?>
        
        <div class="no-results">
            <h2>Nothing Found</h2>
            <p>
                <?php
                if (is_search()) {
                    echo 'Sorry, but nothing matched your search terms. Please try again with different keywords.';
                } else {
                    echo 'It seems we can\'t find what you\'re looking for. Perhaps searching can help.';
                }
                ?>
            </p>
            <?php get_search_form(); ?>
        </div>
        
    <?php endif; ?>
</div>

<?php
// Include sidebar for certain archives
if (should_show_sidebar()) {
    get_sidebar();
}

get_footer();

// Helper functions (in functions.php)
function get_archive_layout_class() {
    if (is_post_type_archive('portfolio')) {
        return 'grid-layout columns-3';
    } elseif (is_category('news') || is_tag()) {
        return 'list-layout with-excerpts';
    } else {
        return 'default-layout';
    }
}

function should_show_sidebar() {
    // No sidebar for portfolio archives
    if (is_post_type_archive('portfolio')) {
        return false;
    }
    // No sidebar for author archives
    if (is_author()) {
        return false;
    }
    // Show sidebar for everything else
    return true;
}
?>

Practice Exercise

Create a custom theme with intelligent template selection:

💻
Try It Now
Build a theme structure that:
  1. Has specific templates for at least 3 different scenarios
  2. Uses template parts to avoid code duplication
  3. Implements conditional logic in archive.php for different post types
  4. Creates a custom page template with selectable options
  5. Properly falls back to index.php when specific templates don't exist
View Solution
// Theme file structure
themes/smart-theme/
├── index.php                 // Ultimate fallback
├── front-page.php           // Home page
├── single.php               // Single posts
├── single-product.php       // Single product posts
├── page.php                 // Single pages
├── archive.php              // All archives
├── archive-product.php      // Product archive
├── category.php             // Category archives
├── search.php               // Search results
├── 404.php                  // Error page
├── header.php
├── footer.php
├── functions.php
├── style.css
├── template-parts/
│   ├── content.php
│   ├── content-single.php
│   ├── content-page.php
│   ├── content-product.php
│   ├── content-excerpt.php
│   └── content-none.php
└── page-templates/
    └── template-portfolio.php

// index.php - Smart fallback
<?php get_header(); ?>

<main id="main" class="site-main">
    <?php
    if (have_posts()) :
        
        // Determine content layout based on context
        if (is_home() && !is_front_page()) : ?>
            <header>
                <h1 class="page-title">Blog</h1>
            </header>
        <?php endif;
        
        echo '<div class="content-area">';
        
        while (have_posts()) : the_post();
            
            // Smart template part selection
            if (is_singular()) {
                get_template_part('template-parts/content', 'single');
            } elseif (is_search()) {
                get_template_part('template-parts/content', 'excerpt');
            } else {
                get_template_part('template-parts/content', get_post_type());
            }
            
        endwhile;
        
        echo '</div>';
        
        the_posts_navigation();
        
    else :
        get_template_part('template-parts/content', 'none');
    endif;
    ?>
</main>

<?php 
get_sidebar();
get_footer();

// single-product.php - Custom post type template
get_header(); ?>

<div class="product-single">
    <?php while (have_posts()) : the_post(); ?>
        <article id="product-<?php the_ID(); ?>" <?php post_class(); ?>>
            <div class="product-images">
                <?php 
                if (has_post_thumbnail()) {
                    the_post_thumbnail('large');
                }
                
                // Product gallery
                $gallery = get_field('product_gallery');
                if ($gallery) : ?>
                    <div class="product-gallery">
                        <?php foreach ($gallery as $image) : ?>
                            <img src="<?php echo esc_url($image['sizes']['thumbnail']); ?>" 
                                 alt="<?php echo esc_attr($image['alt']); ?>">
                        <?php endforeach; ?>
                    </div>
                <?php endif; ?>
            </div>
            
            <div class="product-info">
                <h1><?php the_title(); ?></h1>
                
                <div class="product-price">
                    $<?php echo get_field('price'); ?>
                </div>
                
                <div class="product-description">
                    <?php the_content(); ?>
                </div>
                
                <div class="product-meta">
                    <?php
                    $categories = get_the_terms(get_the_ID(), 'product_category');
                    if ($categories) : ?>
                        <div class="product-categories">
                            <strong>Categories:</strong>
                            <?php foreach ($categories as $category) : ?>
                                <a href="<?php echo get_term_link($category); ?>">
                                    <?php echo $category->name; ?>
                                </a>
                            <?php endforeach; ?>
                        </div>
                    <?php endif; ?>
                </div>
            </div>
        </article>
        
        <!-- Related Products -->
        <?php
        $related = new WP_Query(array(
            'post_type' => 'product',
            'posts_per_page' => 4,
            'post__not_in' => array(get_the_ID()),
            'orderby' => 'rand'
        ));
        
        if ($related->have_posts()) : ?>
            <section class="related-products">
                <h2>Related Products</h2>
                <div class="products-grid">
                    <?php while ($related->have_posts()) : $related->the_post(); ?>
                        <?php get_template_part('template-parts/content', 'product'); ?>
                    <?php endwhile; ?>
                </div>
            </section>
            <?php wp_reset_postdata(); ?>
        <?php endif; ?>
        
    <?php endwhile; ?>
</div>

<?php get_footer(); ?>

Practice Assignment

Master the WordPress Template Hierarchy by completing these tasks:

  • Create a complete theme with at least 10 different template files
  • Implement custom templates for 2 different custom post types
  • Build an intelligent archive.php that handles all archive types differently
  • Create 3 custom page templates with different layouts
  • Use conditional tags to modify template behavior based on context
  • Organize your theme with proper template parts structure
  • Test your template fallback chain by removing templates

Additional Resources