An intermediate roadmap to WordPress development

While scrolling through the posts in r/Wordpress I stumbled across a few asking about what I’m calling “intermediate WordPress development roadmaps,” or “a learning roadmap for developers who have some level of experience with web development, generally, but perhaps not with WordPress specifically.”

I wrote a couple of different versions of this answer, listing both great resources on Udemy and my current understanding of the state of WordPress development, generally. I got tired of copying and pasting, so here are my expanded thoughts in one post.

A quick personal history with WordPress

WordPress was my entry into web dev, so I started writing non-prefixed functions into the functions.php file, then moved into theme development (with the Brad Schiff’s Udemy course—listed below), then moved into more OOP-style development (as I learned vanilla Javascript), and now I’m working on migrating classic themes into the new block theme (aka Gutenberg) era.

Let’s establish some dev dependencies.

Here’s a couple of things I’m assuming about you:

  • You’re decently familiar with either Javascript/Typescript or PHP
    • If you’re familiar with one, you can use ChatGPT to provide the analog for the other (or, you know, just read the documentation).
  • You’re comfortable writing HTML and CSS.
  • You’ve got some knowledge of package managers—namely, npm.
  • You can google things and are somewhat familiar with reading documentation.

As an aside, here’s some quick tips:

  1. Use Local to run WordPress locally.
  2. Here’s a great video for how to install XDebug with VS Code and Local.
  3. You can install WordPress coding standards with composer (php’s package manager)!

General WordPress concepts to get familiar with.

The biggest concepts to know are probably: the WordPress Query & Loop, the template hierarchy, and the power of custom post types.

In WordPress, everything is a post.

At its core, WordPress sees everything as a post. Pages are a post with a type of “page.” Taxonomies (like “Categories” or “Tags”) are also posts with the type of “taxonomy” and a term within said taxonomy is a post with a specific slug. This means we can extend WordPress quickly by creating custom post types. The code is pretty straightforward, so you can either write your own post types or use a plugin like Custom Post Types UI (CPTUI) or Advanced Custom Fields (ACF) to make post types. Whether you write it yourself or use a plugin is preference, and you’ll find both implementations in the wild.

Custom Post Types

However you implement them, custom post types extend your site in powerful ways and also can give your site new routes. For example, you can create a new post type called “work” for a portfolio and all the posts with that type would be found at https://your-site.com/work/the-post-slug. You could extend this “work” post type with its own custom taxonomies, or you could hook it into the WordPress Core’s “Categories” and “Tags” taxonomies, allowing for search results to populate both posts and “work” posts. This, then, leads us into the template hierarchy.

The Template Hierarchy

At their core, WordPress themes have 2 files: index.php and style.css. The index.php file is special: WordPress gives it access to the global $post variable by way of the WordPress loop (which we’ll get into in a minute). In other words, WordPress loops through your database and displays each post according to the template you create inside of this file.

That’s cool, but it wasn’t all that helpful to me since most of the sites I developed had pages, not posts.

The Template Hierarchy looks at your file structure. So, within your wp-content/themes/theme folder (where “theme” is the theme I assume you’re developing), you could place the following (assuming you’ve also created a custom post type called “work”):

  • index.php (required)
  • style.css (required)
  • front-page.php or home.php
    • Overrides index.php when viewing the site’s home page
  • page.php
    • Overrides index.php when viewing a post with the type of “page”
  • page-about.php
    • Overrides page.php when viewing a post with the type of “page” and a slug of “about”
  • single.php
    • the template WordPress uses to display a single post (e.g. https://your-site.com/blog/post-title)
  • archive.php
    • the template WordPress uses to display all posts (e.g. https://your-site.com/blog).
  • single-work.php
    • Overrides the single.php template for posts with the “work” post type (e.g. https://your-site.com/work/project-title)
  • archive-work.php
    • Overrides the archive.php template when displaying all posts with the “work” post type (e.g. https://your-site.com/work/)

The WordPress Loop & Query

WordPress has a lot of global variables and functions in its API, but the most important one is the following:

if ( have_posts() ) {
  while ( have_posts() ) {
    the_post();
  }
} else {
  echo "<p>Sorry, no posts have been found.</p>";
}

Each template has access to the $post variable, which is the current post (or set of posts) in your SQL database. have_posts() checks if the current WordPress (aka SQL) query has returned any posts. the_post() updates the global $post variable inside the while loop so that WordPress’s API works as expected (e.g. the_title() displays the post title like you’d expect).


Overall, I would say there are 2 trains of thought: block theme / full site editing and php + advanced custom fields (ACF) plugin. (There’s also headless WordPress (with WordPress GraphQL plugin) if you’re curious about that.)

Modern WordPress Development: Blocks & Full-Site Editing

With the rise of Visual Builders (Divi, Elementor, and my personal fave, Themeco’s Pro & X Themes), WordPress has made the switch to Blocks and Full-Site Editing (powered, under the hood, by React). The Block Editor (which you might also see as “Gutenberg”) is the main content engine, and it almost totally scraps php as the main development language.

Gutenberg has a lot of quirks, but the main idea is to let people design their own sites. Personally, I think it’s a little clunky (purely from a CMS / content manager perspective), but I’ve seen some really nice-looking sites built (albeit with a simple feature-set) that are easy for a content manager to make edits to. It’s probably about as user-friendly as Squarespace.

The big things to learn here are:

  • the integration of the @wordpress/scripts npm package into your workflow
  • the theme.json and block.json metadata patterns
  • Importing all the block dependencies so WordPress uses them automatically.

There are whole tutorials on this (my favorite is listed below), so I’m not going to go into that, but here’s a high-level overview of the dev cycle.

  1. Themes are powered by html files (that follow the template hierarchy) with special WordPress html comments
  2. Blocks can be rendered by React (“static” blocks) or with php (“dynamic” blocks), which will determine how the markup will be generated.

The benefit of block themes is that a content manager gets a better picture of what the site looks like on the frontend, but the developer has to create both an “editor” view and a “frontend” view (which is a bit annoying).


Classic WordPress: PHP (+ the Advanced Custom Fields Plugin)

There’s a lot to learn here about the WordPress way of doing things (for example get_the_title() versus the_title()), but it’s well-documented and pretty straightforward. To use a block theme (instead of a theme that uses Full-Site editing), you could simply have a page.php file that looks like this:

<?php 
/**
* Page.php file
*
* @package KJR
*/
get_header();
?>
<article>
  <?php
  the_title("<h1>","</h1>");
  the_content();
  ?>
</article>
<?php
get_footer();

The the_content() function echoes the page’s editor, which is Gutenberg (unless you’ve installed the classic WYSIWYG editor). However, if you want to limit content manager’s control over what type of data they can populate a page with, chances are you’ll use the Advanced Custom Fields plugin (or ACF Pro).

Advanced Custom Fields (Pro)

WordPress ships a way to add custom fields to posts, but most devs will simply opt in to using ACF (Pro) to handle adding and managing them. Apparently they’re a nightmare to create yourself (I’ve not tried).

ACF is well-documented and its API is similar to WordPress’s, so it should feel very familiar to use.

Hooks and Filters

If you’re familiar with React Hooks, it’s pretty similar. The WordPress app has a bunch of hooks that get fired in a certain order, and you can also write your own.

There are a lot (thanks to wp-kama for creating a great list of hooks in their call order), but the main idea I want to present here is adding script and style tags to the header.

wp_enqueue_scripts

As of WordPress 6.3, the WordPress + php way of enqueuing scripts and styles is to use a callback function tied to the wp_enqueue_scripts hook.

add_action('wp_enqueue_scripts', 'enqueue_my_custom_script');
function enqueue_my_custom_script() {
  wp_enqueue_script(
    'kj-data', // id for the script
    get_stylesheet_directory_uri() . '/dist/global.js', // src value
    array(), // dependency array
    '1.0', // version
    array( // array or boolean
      'strategy' => 'defer',
      'in_footer' => false
    );
}

For the wp_enqueue_script function, the major change is the final argument: an array versus a boolean. Historically, the boolean controlled whether the script was loaded in the head or the footer of a site (true === load in footer, false was default). The WordPress Core team updated the argument with an overload to allow for an array that gives us access to async/defer values, which is great.

This function would create the following script tag in the <head>:

<script defer type="text/javascript" src="https://your-site.com/wp-content/themes/theme/dist/global.js?ver=1.0" id="kj-data-js" data-wp-strategy="defer"></script>

Udemy WordPress Tutorials:

Leave a Reply