How to Make a Custom WordPress Theme: Theme Files Essentials Image Banner
The Peasant in Debt (1865) - Benjamin Vautier

How to Make a Custom WordPress Theme: Theme Files Essentials

Rol Avatar
by Rol John Torralba —

In this article, we will be exploring how WordPress theme files work, how WordPress treats them, and the basic functions to work with the theme files.

Note: to follow through this guide, you must at least know beginner HTML and CSS. If you are unfamiliar with the two technologies mentioned, read my beginner HTML guide and beginner CSS guide.

As you may already know, a WordPress theme changes your website's appearance, but what exactly is a WordPress theme, and how do you make one?

WordPress Theme Files

At its barest form, you only need three entities:

  1. The folder where your theme files live: The folder's name should reflect your theme's name (e.g. twentytwentythree, vws-bs-starter).
  2. The index.php file: The file that renders the HTML file.
  3. The style.css file: The file that contains your theme information.

PHP and HTML files

PHP is a programming language used by WordPress as part of its technology stack. You can embed HTML inside PHP files (.php). Using PHP to write complex scripts can enhance your website's functionalities, making it a dynamic website.

The index.php file

Think of the index.php file as an entry point to your website. That is, if a user enters your site in their browser, they will see whatever your index.php file renders. Even if the file only contains HTML tags, it is still a valid PHP page that WordPress can render.

The style.css file

This file contains all of your theme's information and CSS styles. You only need the header part which contains all the theme's information— even without any CSS styles —to make your theme valid.

Here's an example of the header part of a style.css file in WordPress:

/*
Theme Name: Twenty Twenty
Theme URI: https://wordpress.org/themes/twentytwenty/
Author: the WordPress team
Author URI: https://wordpress.org/
Description: Our default theme for 2020 is designed to take full advantage of the flexibility of the block editor. Organizations and businesses have the ability to create dynamic landing pages with endless layouts using the group and column blocks. The centered content column and fine-tuned typography also makes it perfect for traditional blogs. Complete editor styles give you a good idea of what your content will look like, even before you publish. You can give your site a personal touch by changing the background colors and the accent color in the Customizer. The colors of all elements on your site are automatically calculated based on the colors you pick, ensuring a high, accessible color contrast for your visitors.
Tags: blog, one-column, custom-background, custom-colors, custom-logo, custom-menu, editor-style, featured-images, footer-widgets, full-width-template, rtl-language-support, sticky-post, theme-options, threaded-comments, translation-ready, block-styles, wide-blocks, accessibility-ready
Version: 1.3
Requires at least: 5.0
Tested up to: 5.4
Requires PHP: 7.0
License: GNU General Public License v2 or later
License URI: http://www.gnu.org/licenses/gpl-2.0.html
Text Domain: twentytwenty
This theme, like WordPress, is licensed under the GPL.
Use it to make something cool, have fun, and share what you've learned with others.
*/

You can read more about what each of the properties does in this WordPress article.

As simple as it may seem, that's all you need to start making a custom WordPress theme.

WordPress Template Files Introduction

As you have learned previously, the index.php file is the main entry point for users visiting your site, but that is only true if that is the only template file present in your theme.

A template file, like index.php, is what WordPress displays based on what page the user is currently at.

Consider the following scenario:

  • You created an About, Contact, and Services page.
  • You published a couple of blog posts.
  • A user visits your Contact page.

WordPress knows a user is viewing your Contact page, but how does it display the contents of your Contact page to the user? That is where template files come in.

If you only have the index.php template file present in your theme, WordPress will display the contents of index.php no matter which page the user is currently at.

Types of Template Files

There are a lot of template files available, but we will be only covering a few. At least few enough to be an effective WordPress developer.

As you have already discovered, there are two types of Post Types in WordPress: the inconveniently named Post and Page.

Posts (blog posts) appear on your home page (blog list) when you first install WordPress. Pages, like About and Contact, don't appear on your blog list, and don't have the ability to set categories, but can be hierarchical.

  • The template file for the Post post type is single.php.
  • while the template file for Page post type is page.php.

If you put both template files with distinct contents in your theme, you should see how WordPress automatically determines which template file is displayed based on whether you're in a blog post or a page.

But what happens to the index.php template? It becomes the fallback template file. If you remove single.php for example, WordPress will use the index.php template when viewing a blog post.

As you can see, WordPress maintains a template hierarchy to determine which template to load when you're in a specific page. In the end, everything falls back to the main template file which is index.php, and that is the reason why it is required.

You can read more about the WordPress Template Hierarchy here.

WordPress Template Files is just a glorified file organization system

We can create a fully functional theme that renders different page templates with only the index.php file.

As absurd as it may sound, it is possible with the help of conditional rendering with PHP. Basically, it's just a series of decision-making:

  • If a user is in a Post post type, display this code.
  • Else, if a user is in a Page post ype, display this code instead.
  • Else, if a user is in a blog list, display this code that lists all my blog posts.
  • And so on...

But why do that and produce thousands of lines of code in one file if you can organize your code nicely through template files.

Special Theme Files

Apart from the template files, WordPress has essential and non-essential theme files. Essential in a sense that it is required if you're planning to release your theme in the WordPress theme repository.

Here are some of the commonly used theme files:

  • header.php - You can add all the header-related code inside this file.
  • footer.php - Similar to header.php, you can add all the footer-related code inside this file.
  • sidebar.php - If your website design includes a sidebar, you may add all the sidebar code here.
  • comments.php - WordPress supports comments, this file contains all the comments-related code (the form and the list).
  • searchform.php - Aside from comments, WordPress has its own search function, you can customize the default search form with this file.

These files act like template files, but unlike template files where WordPress automatically selects and loads depending on context, theme files are only loaded when a related function is manually called.

You have probably figured out by now that these files are used to avoid repeating code throughout your template files.

To elaborate, instead of adding the header-related and footer-related code to every template file you use, why not write the code once in one file and call a one-liner function to render the code on where the function is called.

As an example, assuming you already have the header.php, footer.php, and sidebar.php in your theme, you can easily call them like this:

<!-- page.php -->

<?php get_header(); ?>
<main>
    <h1>This is the page title</h1>
</main>
<?php get_sidebar(); ?>
<?php get_footer(); ?>

WordPress then finds the correct theme file and includes it within your template file. This is what makes PHP and WordPress incredibly powerful. Without much experience with PHP, you're able to figure out what it does.

The three WordPress-specific functions above are wrapped in PHP tags; this is how you embed PHP scripts inside PHP files.

There are more things to uncover to help enhance your theme and improve your development skills, which we will be exploring next through the use of starter themes.

WordPress Starter Themes

These special types of WordPress themes help speed up the development process by setting up the necessary files and helpful functions, getting rid of a lot of overhead. To fully understand WordPress theme development, you need to at least know the basics of PHP.

Without overcomplicating this guide by covering PHP, I'll put you up to speed by learning PHP through WordPress the hard way.

Forget PHP for now, let us explore a sample starter theme first and set it up to make it our own.

If you search the internet for "wordpress starter themes" you'll find hundreds of them, but my favorite is Underscores. Download an Underscores' base theme by generating one. This will set up the starter theme with the details you submit.

If you have downloaded another starter theme, be sure to follow their instructions on how to set up their theme as yours, but it usually goes like this:

  • Rename the starter theme folder.
  • Change the Theme Name attribute in the style.css header.

Now that you have your starter theme somewhere on your computer, you can start using it by uploading it to your WordPress site, here's a video on how to do that.

Starter Theme Files

If you check the starter theme files, you will find the template and theme files we covered previously, which you're already familiar with.

Here is the file structure of an Underscores-based theme named "sample-starter".

sample-starter
├── 404.php
├── LICENSE
├── README.md
├── archive.php
├── comments.php
├── composer.json
├── footer.php
├── functions.php
├── header.php
├── inc
│   ├── custom-header.php
│   ├── customizer.php
│   ├── jetpack.php
│   ├── template-functions.php
│   └── template-tags.php
├── index.php
├── js
│   ├── customizer.js
│   └── navigation.js
├── languages
│   ├── readme.txt
│   └── sample-starter.pot
├── package.json
├── page.php
├── phpcs.xml.dist
├── readme.txt
├── screenshot.png
├── search.php
├── sidebar.php
├── single.php
├── style-rtl.css
├── style.css
└── template-parts
    ├── content-none.php
    ├── content-page.php
    ├── content-search.php
    └── content.php

It's overwhelming seeing a lot of files to work with, but we won't be covering all of them. You only need to understand the files in a higher-level.

To easily make sense of all the files, let's categorize the files starting from what we already covered previously: template files, and theme files.

WordPress maintains a template hierarchy to determine which template to load when you're in a specific page.
Template Files
File name What is it used for?
404.php The template loaded by WordPress when a visitor reaches a page that doesn't exist: Error 404.
archive.php The template used when a user visits an archive page: date, author, category, tag.
index.php The main template file, which all template files fall back into.
page.php Used when a user visits a Page post type.
search.php If a user searches for something using a search form, this template is loaded to display the search results.
single.php Used when a user visits a Post post type.

Moving on with the theme files.

Unlike template files where WordPress automatically selects, theme files are only loaded when a related function is manually called.
Theme Files
File name What is it used for?
comments.php This template is loaded in a location where the comments_template() function is called.
footer.php Loads the template when the get_footer() function is called.
header.php Loads the template when the get_header() function is called.
sidebar.php Loads the template when the get_sidebar() function is called.

Before we move forward to the remaining files, let us explore how there are two types of template files.

Template Files, Dynamic Content and the WordPress Loop

Looking at a higher-level, we now see that there are two types of template files:

  1. Singular templates
  2. List templates

To put it plainly, singular templates are used when a user visits a singular WordPress page; that is, when a user visits one of the pages you made (Contact, About) or a blog post you published.

In contrast, list templates are used for pages that list something. The most obvious example for this is the home page when you first install WordPress, which is actually a blog list page. Other pages that use the list templates are the archive ("show me a list of posts from this author/date/category/tag") and search results page.

We have only covered template loading with static content in mind. For example, if your page.php file only contains the following code, you will get the same content no matter what page you're viewing.

<h1>Welcome to my page</h1>

To display the page title and content dynamically, you will need the WordPress Loop.

<?php
while(have_posts()) // PHP while loop
{ // Opens the code block
    the_post(); // Sets up the post/page's data
    
    the_title(); // Displays the post/page's title coming from the data loaded
    
    the_content(); // Displays the post/page's content body
} // Closes the code block
?>

Here we have a PHP script that displays the title and the content of a page (assuming we're in one). You will see a variation of this code everywhere, especially in template files, which is commonly called the WordPress Loop.

Let's go through it line-by-line.

  • while( have_posts() ) - The while( condition ) control flow statement is a PHP loop that runs the contents of its code block infinitely, unless the condition resolves to false.
  • have_posts() - The have_posts() inside the while() statement is a WordPress function that returns a condition. Specifically, this function checks if a post (or page) exists, returning either true or false.
  • the_post() - This function sets up the post's data for the loop-specific functions to work correctly.
  • the_title() - This function displays the post's title. This is a loop-specific function, that is, functions that will only work correctly inside the loop.
  • the_content() - Another loop-specific function. This function displays the formatted post's content.

Whether you're in a singular or in a list template, the same WordPress Loop is used. Think of it as WordPress providing the data (post or list of posts) to a page through the Loop, and it is up to you what to make of it.

Function and Template Parts

Back to the remaining theme files, if we remove what we have already covered, this is what is left:

sample-starter
├── LICENSE
├── README.md
├── composer.json
├── functions.php
├── inc
│   ├── custom-header.php
│   ├── customizer.php
│   ├── jetpack.php
│   ├── template-functions.php
│   └── template-tags.php
├── js
│   ├── customizer.js
│   └── navigation.js
├── languages
│   ├── readme.txt
│   └── sample-starter.pot
├── package.json
├── phpcs.xml.dist
├── readme.txt
├── screenshot.png
├── style-rtl.css
└── template-parts
    ├── content-none.php
    ├── content-page.php
    ├── content-search.php
    └── content.php

We can further clean it up by skipping the files we're not covering in this guide. The theme would still work without the files, as long as any references to them in the code are removed.

Here are the skipped files:

sample-starter
├── LICENSE         // Theme license information - if you intend to distribute your theme.
├── README.md       // Readme text used by git repositories like github.
├── composer.json   // Used by a PHP package manager called Composer to install PHP packages.
├── languages       // Language-related, so your theme could be translated. functions.php file has a reference to this folder, be sure to remove it when removing this folder.
│   ├── readme.txt
│   └── sample-starter.pot
├── package.json    // Used by a JavaScript package manager called NPM to install JavaScript packages. Like Composer mentioned above.
├── phpcs.xml.dist  // PHP Code Sniffer related file. Used to validate PHP coding standards. This is mainly used to assist you with the WordPress-specific PHP formatting standards, especially if you're planning to release your theme to WordPress.
├── readme.txt      // Used by WordPress to display your theme's information.
├── screenshot.png  // Used by WordPress to display your theme's thumbnail.
├── style-rtl.css   // Styles for right-to-left languaged websites. This is not required, but some languages like Arabic is read from right to left, so it's logical to place the logo on the right side instead of the left which is common for English websites.

And this is what's left:

sample-starter
├── functions.php
├── inc
│   ├── custom-header.php
│   ├── customizer.php
│   ├── jetpack.php
│   ├── template-functions.php
│   └── template-tags.php
├── js
│   ├── customizer.js
│   └── navigation.js
└── template-parts
    ├── content-none.php
    ├── content-page.php
    ├── content-search.php
    └── content.php

Functions

functions.php in WordPress acts like a plugin for your theme. It is where all the theme setups are initiated like setting up the WordPress menu locations, sidebars, and loading additional CSS styles and JavaScript.

Aside from the theme setups, theme developers add custom PHP functions inside this file, and it will be available through all the files in your theme.

In the case of the Underscores starter theme, they've added all the functions they think are necessary to help you speed up theme development with the goal of releasing your theme to WordPress. They have also categorized these functions into individual files, which can be found inside the inc folder.

Function Files.
File name What is it used for?
functions.php Main functions file, where you can find all the necessary functions in setting up your theme.
custom-header.php Functions to set up WordPress custom header: a feature that enables customizing the header of your theme via a GUI in the backend.
customizer.php A boilerplate function to help you add customizer-specific code to improve the WordPress Customizer screen.
jetpack.php Functions to enable or assist you with Jetpack-specific features. Jetpack is a WordPress theme that enhances your website's features.
template-functions.php Functions which enhance the theme by hooking into WordPress. Hooks are a more advanced feature of WordPress development.
template-tags.php Custom functions specific to the theme. One example is a function you can call to output the "posted by {author}" directly.

*Non-emphasized file names are a bit advanced and can be skipped for now.

If you know enough PHP, going through each file would give you at least an idea of what the functions do, which is also easier with the help of code comments.

Scripts

Inside the js folder in the Underscores theme root, you'll find the following JS scripts that are loaded by default:

  • customizer.js - Used in conjunction with inc/customizer.php to improve WordPress Customizer experience.
  • navigation.js - Script to improve the menu experience in mobile and accessibility.

Template Parts

Template Parts is another way to organize code which can be used to dynamically load files.

You can render whatever PHP file in your theme with a single WordPress function:

get_template_part();

Say if you want to render the contents of template-parts/content-page.php you can do it like this.

get_template_part('template-parts/content-page')

// OR

get_template_part('template-parts/content', 'page')

Note how we omitted the .php file extension.

The first example loads content-page.php as is, if it can't find the file, it will render nothing.

The second example loads content-page.php, but if it can't find the file, it will fall back to content.php which should also be inside the template-parts folder.

In the case of Underscores, the authors created four template files inside the template-parts folder.

  • content-none.php - Template to load when there are no posts found. Mainly used in blog lists, archive, and search results pages.
  • content-page.php - Template for loading a Page's content.
  • content-search.php - The template loaded for each post in a search results page.
  • content.php - Template loaded for both the blog list and Single post, and is also the fallback file for all template parts.

Summary

In this article, we've explored the essentials of theme development by delving into the basics of how theme files function. While we haven't delved deep into the code, grasping WordPress's approach to theme files provides a strong foundation for entering the world of theme development.

Ready to build your website?

Curious about how much a website will cost? Get a free quote or check our pricing calculator for a rough estimate.