Theme Anatomy
WordPress theme anatomy: required files, the template hierarchy, functions.php, the loop, and where to put each kind of code.
WordPress — theme anatomy
EXAMPLE
<?php
// ===== Minimum theme structure =====
// my-theme/
// style.css (REQUIRED — header doc block declares the theme)
// index.php (REQUIRED — fallback template)
// functions.php (optional — theme bootstrap + hooks)
// screenshot.png (optional — admin theme picker)
// ... other templates as needed
// ===== style.css header =====
/*
Theme Name: My Theme
Theme URI: https://example.com/my-theme
Author: You
Description: A small custom theme.
Version: 1.0.0
License: GPL-2.0-or-later
Text Domain: my-theme
*/
// ===== Template hierarchy (most common routes) =====
// front-page.php homepage when set in Settings -> Reading
// home.php blog posts index
// single.php single post
// page.php single page
// archive.php archives (category/tag/author/date)
// search.php search results
// 404.php not found
// header.php / footer.php / sidebar.php (partials)
// index.php ultimate fallback
// More specific names win (e.g. category-news.php beats archive.php for the 'news' category).
// ===== functions.php =====
add_action('after_setup_theme', function () {
add_theme_support('title-tag');
add_theme_support('post-thumbnails');
add_theme_support('html5', ['search-form', 'comment-form', 'comment-list', 'gallery', 'caption']);
register_nav_menus([
'primary' => __('Primary', 'my-theme'),
]);
});
add_action('wp_enqueue_scripts', function () {
wp_enqueue_style('my-theme', get_stylesheet_uri(), [], '1.0.0');
});
// ===== A page template using The Loop =====
// page.php
<?php get_header(); ?>
<main>
<?php if (have_posts()) : while (have_posts()) : the_post(); ?>
<article id="post-<?php the_ID(); ?>">
<h1><?php the_title(); ?></h1>
<div><?php the_content(); ?></div>
</article>
<?php endwhile; endif; ?>
</main>
<?php get_footer(); ?>
// ===== header.php =====
<!DOCTYPE html>
<html <?php language_attributes(); ?>>
<head>
<meta charset="<?php bloginfo('charset'); ?>">
<meta name="viewport" content="width=device-width, initial-scale=1">
<?php wp_head(); ?>
</head>
<body <?php body_class(); ?>>
<?php wp_body_open(); ?>
<header>
<a href="<?php echo esc_url(home_url('/')); ?>"><?php bloginfo('name'); ?></a>
<?php wp_nav_menu(['theme_location' => 'primary']); ?>
</header>
// ===== footer.php =====
<footer>(C) <?php echo date('Y'); ?> <?php bloginfo('name'); ?></footer>
<?php wp_footer(); ?>
</body></html>
// ===== Block themes (WordPress 6.x+) =====
// Block themes replace PHP templates with HTML files in /templates/ and /parts/,
// configured via theme.json. The hierarchy still applies; just .html instead of .php.
// ===== Patterns to internalise =====
// - Use wp_enqueue_style / wp_enqueue_script; never hard-code <link rel> tags
// - Escape output: esc_html, esc_attr, esc_url, wp_kses_post
// - Use a child theme when customising a parent
// - Block themes (theme.json) for new sites in 2026
// ===== Pitfalls =====
// - Editing parent theme files directly (lost on update)
// - Forgetting wp_head() / wp_footer() -> plugins/CSS break
// - Direct DB queries in templates -> slow; use WP_Query
// - Hard-coding URLs; use home_url(), site_url(), get_template_directory_uri()
Why it matters
A theme is style.css + index.php at minimum, with templates layered by hierarchy and behaviour added via functions.php. Enqueue assets correctly, escape output, use The Loop, and prefer child themes for customisation. Block themes are the modern path; classic themes are still everywhere.
Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.
Example
Example
/*
style.css — theme metadata + global CSS
functions.php — boot code, hooks, enqueue
index.php — fallback template
header.php / footer.php
page.php / single.php / archive.php
*/
Try it Yourself »
Discussion
Loading…