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.
Lessons
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().
<?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().
<?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();| Archive | the_archive_title() prints |
|---|---|
| Category "News" | Category: News |
| Tag "php" | Tag: php |
| Author "Asha" | Author: Asha |
| October 2026 | Month: October 2026 |
| A custom post type archive | Archives: 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.
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' );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": theWP_Termon a category archive, theWP_Useron an author archive, theWP_Poston a single post. It is the clean way to get archive details.
single.php and archive.php as above.In accordance with Section 6 & Section 7 of the DPDP Act 2023, third-party analytics and marketing trackers are strictly gated behind your affirmative consent. You may withdraw consent at any time under Section 6(4).
Essential for session authentication (ci_session), Cross-Site Request Forgery security (csrf_cookie_name), and Cloudflare DDoS/bot prevention. Cannot be disabled.
Gated under DPDP Section 7. Allows Google Analytics 4 (Google Consent Mode v2) to measure aggregate, anonymized tool usage.
Enables browser local storage to preserve your theme selection (dark/light mode) and code editor layout between visits.
Used to display developer sponsorships and native tool partner notices.