WordPressTheme DevelopmentPHPTutorial

WordPress Theme Structure: What Every File Does and Why It Matters

May 23, 202610 min readBy Tom Bakker

A WordPress theme is a folder of files. WordPress assembles them on every page request. If you know what each file does, you can build, modify, or debug any theme. If you don't, you're guessing. This is the complete reference.

The minimum required files

Two files are all you need for WordPress to recognize a theme. That's it.

style.css

WordPress reads the comment block at the top of this file to register the theme. The comment block must include at least a Theme Name line.

/*
Theme Name: My Theme
Author: Your Name
Description: Theme description
Version: 1.0
*/

Your actual CSS can live here or be enqueued from functions.php. Most developers use functions.php to load stylesheets. That gives plugins and child themes more control.

index.php

This is the fallback template. WordPress uses it when no more specific template file exists. Every theme needs one. It's the last line of defense in the template hierarchy.

Two files, and WordPress shows your theme in the Appearance menu. Everything else builds on top of this.

The core template files

Most themes add several more files beyond the minimum. Here's what each one does.

header.php

This file contains everything from the opening <html> tag through the opening <body> tag. It includes your <head> section: meta tags, title, and enqueued stylesheets.

One thing you must not skip: <?php wp_head(); ?> before the closing </head> tag. Plugins hook into wp_head() to inject scripts and styles. Leave it out and things break.

You pull the header into other templates with <?php get_header(); ?>.

footer.php

The footer contains closing tags, footer HTML, and scripts. Like header.php, it has one non-negotiable line: <?php wp_footer(); ?> before the closing </body> tag.

Many plugins, including analytics tools and chat widgets, hook into wp_footer(). Skip it and those plugins stop working. Use <?php get_footer(); ?> to load it from other templates.

functions.php

WordPress loads this file automatically on every request. It's not a template. It's a PHP file for theme setup code.

You use it to enqueue scripts and styles, register navigation menus, add theme support, and define custom functions. It runs before any template file.

There's a whole section on functions.php below. It does a lot.

index.php as The Loop

When WordPress falls back to index.php, it expects to find The Loop. The Loop is WordPress's pattern for outputting posts.

<?php if (have_posts()) : while (have_posts()) : the_post(); ?>
  <!-- post output here -->
<?php endwhile; endif; ?>

More on The Loop in its own section below.

The template hierarchy

WordPress picks which template file to use based on what's being requested. It searches for files in a specific order and uses the first one it finds.

Here's the priority order for common page types. WordPress looks for each file in order and stops at the first match.

Page typeTemplate files WordPress checks (highest priority first)
Front pagefront-page.php, home.php, index.php
Single postsingle-{post-type}-{slug}.php, single-{post-type}.php, single.php, singular.php, index.php
Static page{custom-template}.php, page-{slug}.php, page-{id}.php, page.php, singular.php, index.php
Category archivecategory-{slug}.php, category-{id}.php, category.php, archive.php, index.php
Tag archivetag-{slug}.php, tag-{id}.php, tag.php, archive.php, index.php
Author archiveauthor-{nicename}.php, author-{id}.php, author.php, archive.php, index.php
Date archivedate.php, archive.php, index.php
Search resultssearch.php, index.php
404 page404.php, index.php

For most themes you need six files: front-page.php, page.php, single.php, archive.php, search.php, and 404.php. Everything else falls back gracefully to one of these.

The hierarchy is what makes WordPress themes flexible. You don't need a file for every situation. You only create files when you need different output. This is worth understanding early on in your work with the HTML to WordPress theme conversion guide.

theme.json (WordPress 5.8+)

This file controls global styles and settings without CSS. It's optional but very useful.

Here's the problem it solves. Without theme.json, WordPress loads its own block styles. Those styles override your typography on h1, p, a, and ul elements. Your carefully written CSS gets overridden and you spend time debugging why headings look wrong.

With theme.json, you start clean. Here's a minimal version that disables WordPress's default block styles:

{
  "version": 3,
  "settings": {
    "appearanceTools": false,
    "useRootPaddingAwareAlignments": false
  },
  "styles": {
    "spacing": {
      "blockGap": null
    }
  }
}

The most important uses of theme.json:

  • Set settings.appearanceTools to false to stop WP from injecting its own block CSS
  • Define content width and wide width for the block editor
  • Set a color palette and font sizes available in Gutenberg

If you're converting a static site to WordPress and want to keep your existing CSS intact, theme.json is often the first thing to configure. See the guide on converting a static site to WordPress for more on that workflow.

functions.php in detail

This file does more work than any other in your theme. Let's break it down.

Enqueuing CSS and JavaScript correctly

Never link CSS directly in header.php. Use wp_enqueue_style() in functions.php instead.

Direct links bypass WordPress's dependency system. Plugins and child themes can't override them. It's the wrong approach.

function mytheme_enqueue() {
  wp_enqueue_style(
    'mytheme-main',
    get_template_directory_uri() . '/assets/css/main.css',
    [],
    '1.0'
  );

  wp_enqueue_script(
    'mytheme-main',
    get_template_directory_uri() . '/assets/js/main.js',
    [],
    '1.0',
    true
  );
}
add_action('wp_enqueue_scripts', 'mytheme_enqueue');

The second parameter to wp_enqueue_style() is the URL path. Use get_template_directory_uri() to build the correct URL. The last true in wp_enqueue_script() loads the script in the footer.

Registering navigation menus

You register menus in functions.php and output them in templates. This lets site admins assign menu items from the WordPress admin.

function mytheme_setup() {
  register_nav_menus([
    'primary' => 'Primary Navigation',
    'footer'  => 'Footer Navigation',
  ]);
}
add_action('after_setup_theme', 'mytheme_setup');

Then in header.php, you output the menu with:

<?php wp_nav_menu(['theme_location' => 'primary']); ?>

Adding theme support

Theme support declarations tell WordPress what features your theme can handle. Add these inside the same after_setup_theme action:

  • add_theme_support('title-tag'): lets WordPress manage the <title> tag. Don't hardcode it in header.php.
  • add_theme_support('post-thumbnails'): enables featured images on posts and pages.
  • add_theme_support('html5', ['search-form', 'comment-form', 'gallery']): produces clean HTML5 output for built-in WordPress components.

The WordPress Loop

The Loop is how WordPress outputs content. Every template that displays posts or pages uses it.

<?php if (have_posts()) : ?>
  <?php while (have_posts()) : the_post(); ?>
    <h2><?php the_title(); ?></h2>
    <?php the_content(); ?>
  <?php endwhile; ?>
<?php endif; ?>

have_posts() checks if there's content to display. the_post() sets up the global post data. After that, you can use template tags to output content.

Common template tags inside the loop:

  • the_title(): outputs the post or page title
  • the_content(): outputs the full content
  • the_excerpt(): outputs the excerpt or an auto-generated summary
  • the_permalink(): outputs the URL to the post
  • the_post_thumbnail(): outputs the featured image

One thing that trips people up: even on static pages like your homepage, you still use The Loop. A page has one post object. The Loop runs once and outputs it. Don't skip The Loop on front-page.php.

get_template_part() and code reuse

Templates get long. get_template_part() splits repeated HTML into separate partial files.

<?php get_template_part('template-parts/hero'); ?>

This loads template-parts/hero.php from your theme folder. You can use this anywhere in any template file.

Common partials you might create:

  • template-parts/hero.php: homepage hero section
  • template-parts/card.php: post card used in archive and search results
  • template-parts/cta.php: call-to-action block used across multiple pages

This keeps your templates short and focused. It's easier to maintain and easier to debug. When something looks wrong in the hero, you go to one file.

How automated theme generation maps to this structure

When StaticToWP converts a live URL to a WordPress theme, it generates all the files described above. Here's what each generated file contains and why.

  • front-page.php: the captured homepage HTML, wrapped in get_header() and get_footer() calls
  • header.php: the extracted <head> content with wp_head() injected before </head>
  • footer.php: the extracted footer HTML with wp_footer() injected before </body>
  • functions.php: enqueues main.css and any bundled scripts, registers nav menus, adds theme support declarations
  • theme.json: disables WordPress default block styles so they don't override captured CSS
  • assets/css/main.css: all captured computed CSS from the original site
  • assets/js/: bundled external scripts
  • page.php, single.php, archive.php: inner page templates that inherit the design's typography and layout

Understanding this structure lets you customize the generated theme. If a heading style looks off, check theme.json first. If a script isn't loading, check functions.php. If the wrong layout shows on a category page, check whether category.php or archive.php exists and what's in it.

The manual vs automated WordPress theme development guide covers when to generate a theme and when to build one from scratch.

Frequently asked questions

Do I need all these files for every theme?

No. The minimum is style.css and index.php. WordPress falls back to index.php for anything it can't find a more specific template for. Add files as you need them. A simple marketing site might only need style.css, functions.php, header.php, footer.php, front-page.php, and page.php.

What's the difference between a classic theme and a block theme?

Classic themes use PHP template files and functions.php for setup. That's what this guide covers. Block themes use HTML block template files instead of PHP templates. Block themes rely on theme.json for nearly everything. Both approaches are fully supported. Classic themes are still widely used and aren't going away.

Where do custom CSS changes go?

Global styles go in style.css or your enqueued stylesheet. Block editor settings go in theme.json. The WordPress Customizer has an "Additional CSS" field for quick overrides, but that CSS lives in the database and is harder to version control. Put any persistent changes in style.css where you can track them in Git.

Can I add custom post types in functions.php?

Yes. register_post_type() inside functions.php is the standard approach. It works. The downside: if you switch themes, you lose those post type registrations. For post types that hold real content, use a plugin like CPT UI instead. That keeps your content accessible regardless of which theme is active.

Share this post

Tom Bakker

Founder, StaticToWP

Tom built StaticToWP after spending years converting static HTML and AI-generated designs to WordPress by hand. He got tired of doing it manually on every project, so he automated the process. He has converted hundreds of sites across Webflow, Lovable, v0, and plain HTML.

Stay in the loop

New guides, straight to your inbox.

No spam. Useful stuff only. Unsubscribe any time.

Skip the manual work

Paste a URL. Get a WordPress theme.

Any static site, Webflow export, or AI-generated design converted to a production-ready WordPress theme in under a minute. Every CSS rule, animation, and font intact.

Try it free

Free to convert · $499 to download