Skip to main content

Course Progress

Loading...

Main Template Files

Duration: 55 minutes
Module 5: Session 2

Learning Objectives

  • Master the core template files in WordPress themes
  • Understand the purpose and structure of each main template
  • Learn how to properly implement header.php and footer.php
  • Create effective sidebar.php templates
  • Build specialized templates for different content types
  • Implement proper WordPress hooks in templates

Introduction

Every WordPress theme is built from a collection of template files that work together like pieces of a puzzle. Each file has a specific role - think of them as specialized workers in a factory, each responsible for a particular part of the final product.

🏗️
Building Blocks Analogy
If your theme is a house, then header.php is the roof, footer.php is the foundation, sidebar.php is the supporting walls, and the content templates are the rooms where your visitors spend their time!

Template File Overview

Let's visualize how template files come together to create a complete page:

header.php

Site Header

Logo, Navigation, Search

index.php / single.php / page.php

Main Content Area

The Loop, Posts, Pages

The index.php File

The index.php is the most important template file - it's the ultimate fallback that must exist in every theme:

📄 index.php REQUIRED

Purpose: The main template file and ultimate fallback for all content

Used for: Any content when no more specific template exists

Must include: The Loop, proper HTML structure

<?php
/**
 * The main template file
 *
 * @package My_Theme
 */

get_header(); ?>

<div id="primary" class="content-area">
    <main id="main" class="site-main">
        
        <?php
        if ( have_posts() ) :
            
            // Check what type of content we're displaying
            if ( is_home() && ! is_front_page() ) :
                ?>
                <header>
                    <h1 class="page-title screen-reader-text">
                        <?php single_post_title(); ?>
                    </h1>
                </header>
                <?php
            endif;
            
            // Start the Loop
            while ( have_posts() ) :
                the_post();
                
                /*
                 * Include the Post-Type-specific template for the content.
                 * If you want to override this in a child theme, then include a file
                 * called content-___.php (where ___ is the Post Type name) and that
                 * will be used instead.
                 */
                get_template_part( 'template-parts/content', get_post_type() );
                
            endwhile;
            
            // Navigation
            the_posts_navigation();
            
        else :
            
            // No content found
            get_template_part( 'template-parts/content', 'none' );
            
        endif;
        ?>
        
    </main>
</div>

<?php
get_sidebar();
get_footer();

The header.php File

The header.php contains everything from the DOCTYPE to the opening of your main content area:

Anatomy of header.php

1. DOCTYPE and Opening Tags

HTML5 DOCTYPE, language attributes, and opening html/head tags

2. Head Section

Meta tags, title, stylesheets (via wp_head())

3. Body Opening

Body tag with body_class(), site header, navigation

4. wp_body_open()

Hook for plugins/themes to inject code after body tag

<?php
/**
 * The header for our theme
 *
 * @package My_Theme
 */
?>
<!DOCTYPE html>
<html <?php language_attributes(); ?>>
<head>
    <meta charset="<?php bloginfo( 'charset' ); ?>">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="profile" href="https://gmpg.org/xfn/11">
    
    <?php wp_head(); ?>
</head>

<body <?php body_class(); ?>>
<?php wp_body_open(); ?>
<div id="page" class="site">
    <a class="skip-link screen-reader-text" href="#primary">
        <?php esc_html_e( 'Skip to content', 'my-theme' ); ?>
    </a>

    <header id="masthead" class="site-header">
        <div class="site-branding">
            <?php
            the_custom_logo();
            if ( is_front_page() && is_home() ) :
                ?>
                <h1 class="site-title">
                    <a href="<?php echo esc_url( home_url( '/' ) ); ?>" rel="home">
                        <?php bloginfo( 'name' ); ?>
                    </a>
                </h1>
                <?php
            else :
                ?>
                <p class="site-title">
                    <a href="<?php echo esc_url( home_url( '/' ) ); ?>" rel="home">
                        <?php bloginfo( 'name' ); ?>
                    </a>
                </p>
                <?php
            endif;
            
            $description = get_bloginfo( 'description', 'display' );
            if ( $description || is_customize_preview() ) :
                ?>
                <p class="site-description">
                    <?php echo $description; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
                </p>
            <?php endif; ?>
        </div>

        <nav id="site-navigation" class="main-navigation">
            <button class="menu-toggle" aria-controls="primary-menu" aria-expanded="false">
                <?php esc_html_e( 'Primary Menu', 'my-theme' ); ?>
            </button>
            <?php
            wp_nav_menu(
                array(
                    'theme_location' => 'menu-1',
                    'menu_id'        => 'primary-menu',
                )
            );
            ?>
        </nav>
    </header>

    <div id="content" class="site-content">

🔑 Critical Functions in header.php

  • language_attributes() - Outputs language attributes for the html tag
  • bloginfo( 'charset' ) - Gets the site's character encoding
  • wp_head() - Essential hook for styles, scripts, and meta tags
  • body_class() - Adds contextual classes to the body tag
  • wp_body_open() - Hook right after opening body tag (WP 5.2+)

The footer.php File

The footer.php closes all the tags opened in header.php and adds footer content:

<?php
/**
 * The template for displaying the footer
 *
 * @package My_Theme
 */
?>

    </div><!-- #content -->

    <footer id="colophon" class="site-footer">
        <div class="site-info">
            <a href="<?php echo esc_url( __( 'https://wordpress.org/', 'my-theme' ) ); ?>">
                <?php
                /* translators: %s: CMS name, i.e. WordPress. */
                printf( esc_html__( 'Proudly powered by %s', 'my-theme' ), 'WordPress' );
                ?>
            </a>
            <span class="sep"> | </span>
            <?php
            /* translators: 1: Theme name, 2: Theme author. */
            printf( esc_html__( 'Theme: %1$s by %2$s.', 'my-theme' ), 'my-theme', '<a href="https://example.com">Your Name</a>' );
            ?>
        </div>

        <?php if ( has_nav_menu( 'footer' ) ) : ?>
            <nav class="footer-navigation" aria-label="<?php esc_attr_e( 'Footer Menu', 'my-theme' ); ?>">
                <?php
                wp_nav_menu(
                    array(
                        'theme_location' => 'footer',
                        'menu_class'     => 'footer-menu',
                        'depth'          => 1,
                    )
                );
                ?>
            </nav>
        <?php endif; ?>
    </footer>

</div><!-- #page -->

<?php wp_footer(); ?>

</body>
</html>
⚠️
Never Forget wp_footer()
The wp_footer() function is crucial! It's where WordPress and plugins output scripts and other necessary code. Without it, many features won't work properly.

The sidebar.php File

Sidebars are widget-ready areas that typically appear alongside your main content:

sidebar.php

<?php
/**
 * The sidebar containing the main widget area
 *
 * @package My_Theme
 */

if ( ! is_active_sidebar( 'sidebar-1' ) ) {
    return;
}
?>

<aside id="secondary" class="widget-area">
    <?php dynamic_sidebar( 'sidebar-1' ); ?>
</aside>

Registering in functions.php

function my_theme_widgets_init() {
    register_sidebar( array(
        'name'          => __( 'Sidebar', 'my-theme' ),
        'id'            => 'sidebar-1',
        'description'   => __( 'Add widgets here.', 'my-theme' ),
        'before_widget' => '<section id="%1$s" class="widget %2$s">',
        'after_widget'  => '</section>',
        'before_title'  => '<h2 class="widget-title">',
        'after_title'   => '</h2>',
    ) );
}
add_action( 'widgets_init', 'my_theme_widgets_init' );

Multiple Sidebars

You can create different sidebars for different sections:

sidebar.php
Default sidebar
sidebar-shop.php
Shop pages sidebar
sidebar-blog.php
Blog sidebar
sidebar-footer.php
Footer widgets
<?php
// Call specific sidebars
get_sidebar();           // Loads sidebar.php
get_sidebar( 'shop' );   // Loads sidebar-shop.php
get_sidebar( 'blog' );   // Loads sidebar-blog.php
get_sidebar( 'footer' ); // Loads sidebar-footer.php

Content Template Files

These templates handle specific types of content:

📝 single.php

Purpose: Display individual blog posts

Features: Full content, comments, metadata

<?php
get_header(); ?>

<main id="primary">
    <?php
    while ( have_posts() ) :
        the_post();
        
        get_template_part( 'template-parts/content', 'single' );
        
        the_post_navigation();
        
        // If comments are open or we have at least one comment
        if ( comments_open() || get_comments_number() ) :
            comments_template();
        endif;
        
    endwhile;
    ?>
</main>

<?php
get_sidebar();
get_footer();

📄 page.php

Purpose: Display static pages

Features: Page content, optional comments

<?php
get_header(); ?>

<main id="primary">
    <?php
    while ( have_posts() ) :
        the_post();
        
        get_template_part( 'template-parts/content', 'page' );
        
        // If comments are open or we have at least one comment
        if ( comments_open() || get_comments_number() ) :
            comments_template();
        endif;
        
    endwhile;
    ?>
</main>

<?php
get_sidebar();
get_footer();

📚 archive.php

Purpose: Display post archives

Features: Archive title, post list

<?php
get_header(); ?>

<main id="primary">
    <?php if ( have_posts() ) : ?>
        
        <header class="page-header">
            <?php
            the_archive_title( '<h1 class="page-title">', '</h1>' );
            the_archive_description( '<div class="archive-description">', '</div>' );
            ?>
        </header>
        
        <?php
        while ( have_posts() ) :
            the_post();
            get_template_part( 'template-parts/content', get_post_type() );
        endwhile;
        
        the_posts_navigation();
        
    else :
        get_template_part( 'template-parts/content', 'none' );
    endif;
    ?>
</main>

<?php
get_sidebar();
get_footer();

🔍 search.php

Purpose: Display search results

Features: Search query, results list

<?php
get_header(); ?>

<main id="primary">
    <?php if ( have_posts() ) : ?>
        
        <header class="page-header">
            <h1 class="page-title">
                <?php
                printf( esc_html__( 'Search Results for: %s', 'my-theme' ), 
                    '<span>' . get_search_query() . '</span>' );
                ?>
            </h1>
        </header>
        
        <?php
        while ( have_posts() ) :
            the_post();
            get_template_part( 'template-parts/content', 'search' );
        endwhile;
        
        the_posts_navigation();
        
    else :
        get_template_part( 'template-parts/content', 'none' );
    endif;
    ?>
</main>

<?php
get_sidebar();
get_footer();

404.php

Purpose: Display 404 error page

Features: Error message, search form

<?php
get_header(); ?>

<main id="primary">
    <section class="error-404 not-found">
        <header class="page-header">
            <h1 class="page-title">
                <?php esc_html_e( 'Oops! That page can’t be found.', 'my-theme' ); ?>
            </h1>
        </header>
        
        <div class="page-content">
            <p><?php esc_html_e( 'It looks like nothing was found at this location. Maybe try a search?', 'my-theme' ); ?></p>
            
            <?php get_search_form(); ?>
            
            <?php the_widget( 'WP_Widget_Recent_Posts' ); ?>
        </div>
    </section>
</main>

<?php
get_footer();

💬 comments.php

Purpose: Display comments section

Features: Comment list, comment form

<?php
/**
 * The template for displaying comments
 */

// If the current post is protected by a password and
// the visitor has not yet entered the password, return early
if ( post_password_required() ) {
    return;
}
?>

<div id="comments" class="comments-area">
    
    <?php if ( have_comments() ) : ?>
        <h2 class="comments-title">
            <?php
            $comment_count = get_comments_number();
            if ( '1' === $comment_count ) {
                printf( esc_html__( 'One thought on “%1$s”', 'my-theme' ), 
                    '<span>' . wp_kses_post( get_the_title() ) . '</span>' );
            } else {
                printf( 
                    esc_html( _nx( '%1$s thought on “%2$s”', '%1$s thoughts on “%2$s”', $comment_count, 'comments title', 'my-theme' ) ),
                    number_format_i18n( $comment_count ),
                    '<span>' . wp_kses_post( get_the_title() ) . '</span>'
                );
            }
            ?>
        </h2>
        
        <ol class="comment-list">
            <?php
            wp_list_comments( array(
                'style'      => 'ol',
                'short_ping' => true,
            ) );
            ?>
        </ol>
        
        <?php
        the_comments_navigation();
        
    endif; // Check for have_comments()
    
    // If comments are closed and there are comments
    if ( ! comments_open() ) :
        ?>
        <p class="no-comments">
            <?php esc_html_e( 'Comments are closed.', 'my-theme' ); ?>
        </p>
        <?php
    endif;
    
    comment_form();
    ?>
    
</div>

Template File Flow

Understanding how these files work together is crucial:

Typical Page Load Sequence

1. WordPress Core
Determines template
2. Template File
e.g., single.php
3. get_header()
Loads header.php
4. The Loop
Content output
5. get_sidebar()
Loads sidebar.php
6. get_footer()
Loads footer.php

Template Files Best Practices

  • Always include wp_head() and wp_footer(): These are essential hooks
  • Check if sidebars are active: Don't output empty sidebar containers
  • Use proper escaping: esc_html(), esc_url(), esc_attr() for security
  • Make templates translation-ready: Use __() and _e() functions
  • Include skip links: For accessibility, add "Skip to content" links
  • Use semantic HTML5: header, nav, main, aside, footer tags
  • Keep templates DRY: Use get_template_part() for reusable sections
  • Comment your code: Explain complex logic for future reference

Practice Exercise

Let's build a complete set of main template files:

💻
Template Building Challenge
  1. Create a proper header.php with navigation menu support
  2. Build a footer.php with widget area and copyright info
  3. Create sidebar.php with widget area check
  4. Build single.php for blog posts with comments
  5. Create page.php for static pages
  6. Add archive.php with proper archive titles
  7. Create a custom 404.php with helpful content
  8. Test all templates with different content types

Additional Resources