SponsoredHosinger Special Offer

WP WordPress Theme Development

Build classic and block WordPress themes from scratch: the template hierarchy, The Loop, the_title() and the_content(), template parts, menus, the Customizer, child themes and theme.json.

Single, Archive, Category and Author Templates

WordPress Theme Development Lesson 17 of 30 ~5 min read

Overview

Two templates do most of the work on a blog: single.php shows one post in full, and archive.php lists posts for any category, tag, author or date — adapting its heading automatically with the_archive_title().

single.php

<?php
/**
 * The template for displaying a single post.
 */
get_header();
?>

<main id="main" class="site-main">

    <?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>' ); ?>
                <div class="entry-meta"><?php gdc_starter_posted_on(); ?></div>
            </header>

            <?php if ( has_post_thumbnail() ) : ?>
                <figure class="post-thumbnail"><?php the_post_thumbnail( 'large' ); ?></figure>
            <?php endif; ?>

            <div class="entry-content">
                <?php the_content(); ?>
                <?php wp_link_pages(); ?>
            </div>

            <footer class="entry-footer">
                <?php the_tags( '<span class="tags-links">', ', ', '</span>' ); ?>
                <?php edit_post_link( esc_html__( 'Edit', 'gdc-starter' ), '<span class="edit-link">', '</span>' ); ?>
            </footer>
        </article>

        <?php
        the_post_navigation(
            array(
                'prev_text' => '<span class="nav-subtitle">' . esc_html__( 'Previous:', 'gdc-starter' ) . '</span> <span class="nav-title">%title</span>',
                'next_text' => '<span class="nav-subtitle">' . esc_html__( 'Next:', 'gdc-starter' ) . '</span> <span class="nav-title">%title</span>',
            )
        );

        if ( comments_open() || get_comments_number() ) {
            comments_template();
        }
        ?>

    <?php endwhile; ?>

</main>

<?php
get_sidebar();
get_footer();

the_post_navigation() links to the previous and next posts by date; %title is replaced with each post's title. The while loop still matters even though there is only one post — it is what calls the_post().

archive.php

<?php
/**
 * The template for category, tag, author, date and taxonomy archives.
 */
get_header();
?>

<main id="main" class="site-main">

    <?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(); ?>
            <?php get_template_part( 'template-parts/content', get_post_type() ); ?>
        <?php endwhile; ?>

        <?php the_posts_pagination(); ?>

    <?php else : ?>
        <?php get_template_part( 'template-parts/content', 'none' ); ?>
    <?php endif; ?>

</main>

<?php
get_sidebar();
get_footer();

the_archive_title() adapts automatically

Archivethe_archive_title() prints
Category "News"Category: News
Tag "php"Tag: php
Author "Asha"Author: Asha
October 2026Month: October 2026
A custom post type archiveArchives: Books

the_archive_description() prints the description the owner wrote for that category, tag or author — a great place for SEO-friendly introductory text that lives in the dashboard, not in your code.

Removing the "Category:" prefix

Many designs want just "News". Filter the prefix away instead of rebuilding the whole title:

<?php
function gdc_starter_archive_title_prefix( $prefix ) {
    if ( is_category() || is_tag() ) {
        return '';
    }
    return $prefix;
}
add_filter( 'get_the_archive_title_prefix', 'gdc_starter_archive_title_prefix' );

An author archive with a bio

For multi-author blogs, create author.php. It inherits from archive.php in the hierarchy, so add only what differs:

<?php
get_header();
$author = get_queried_object(); // The WP_User being viewed.
?>

<main id="main" class="site-main">
    <header class="author-header">
        <?php echo get_avatar( $author->ID, 96 ); ?>
        <h1 class="page-title"><?php echo esc_html( $author->display_name ); ?></h1>
        <?php if ( $author->description ) : ?>
            <p class="author-bio"><?php echo esc_html( $author->description ); ?></p>
        <?php endif; ?>
    </header>

    <?php while ( have_posts() ) : the_post(); ?>
        <?php get_template_part( 'template-parts/content' ); ?>
    <?php endwhile; ?>

    <?php the_posts_pagination(); ?>
</main>

<?php
get_footer();
Tip: get_queried_object() returns whatever the current page is "about": the WP_Term on a category archive, the WP_User on an author archive, the WP_Post on a single post. It is the clean way to get archive details.

Practice

  • Build single.php and archive.php as above.
  • Visit a category, a tag, an author and a monthly archive and watch the title change.
  • Add a description to a category in the dashboard and see it appear.
Create a free account to save which lessons you've finished. Save my progress