Learn WordPress from Scratch

WordPress Theme Development Basics

Lesson 5 of 5 2 min read Updated 28 September 2026

What a theme needs

A minimal classic theme needs a folder in wp-content/themes/ with two files:

  • style.css with a header comment
  • index.php
/*
Theme Name: My First Theme
Version: 1.0
*/

The template hierarchy

WordPress chooses which template file to use based on the request. Examples:

RequestTemplate used (first found)
Single postsingle-{type}.php, single.php, singular.php, index.php
Pagepage-{slug}.php, page.php, singular.php, index.php
Categorycategory-{slug}.php, category.php, archive.php
Homefront-page.php, home.php, index.php

The Loop

The Loop displays posts.

<?php get_header(); ?>
<main>
<?php if (have_posts()) : while (have_posts()) : the_post(); ?>
  <article>
    <h2><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></h2>
    <?php the_excerpt(); ?>
  </article>
<?php endwhile; else : ?>
  <p>No posts found.</p>
<?php endif; ?>
</main>
<?php get_footer(); ?>

Include wp_head() before </head> and wp_footer() before </body>. Plugins and your scripts depend on them.

functions.php

<?php
function mytheme_setup() {
    add_theme_support('title-tag');
    add_theme_support('post-thumbnails');
    register_nav_menus(['primary' => 'Primary Menu']);
}
add_action('after_setup_theme', 'mytheme_setup');

function mytheme_assets() {
    wp_enqueue_style('mytheme', get_stylesheet_uri(), [], '1.0');
}
add_action('wp_enqueue_scripts', 'mytheme_assets');

Always load CSS and JavaScript with wp_enqueue_style and wp_enqueue_script.

A custom post type

add_action('init', function () {
    register_post_type('note', [
        'label' => 'Notes',
        'public' => true,
        'has_archive' => true,
        'supports' => ['title', 'editor', 'thumbnail'],
        'show_in_rest' => true,
    ]);
});

Flush permalinks (Settings > Permalinks > Save) after registering it.

Security rules for theme developers

  • Escape output: esc_html(), esc_attr(), esc_url().
  • Sanitise input and verify nonces on forms.
  • Check capabilities with current_user_can().

Practice

Build a small theme with a header, a menu, a loop and a custom post type called Projects.