← Back to all projects

LEARN WORDPRESS DEEP DIVE

Learn WordPress: From Zero to Development Master

Goal: Deeply understand the WordPress ecosystem—from its core architecture and database schema to modern theme/plugin development, the REST API, and professional deployment workflows.


Why Learn WordPress Development?

WordPress powers over 43% of the web. While often seen as just a “blogging platform,” it’s a powerful and flexible Content Management System (CMS) and application framework. Mastering its development unlocks immense opportunities, from building custom websites for clients to creating commercial themes and plugins, or leveraging it as a headless backend for modern web applications.

After completing these projects, you will:

  • Understand the entire WordPress request lifecycle, from URL to rendered page.
  • Navigate and manipulate the WordPress database with confidence.
  • Build professional, modern themes and plugins from scratch.
  • Master the Block Editor (Gutenberg) and create custom blocks.
  • Use WP-CLI to manage WordPress environments efficiently.
  • Extend the WordPress REST API for headless applications.
  • Implement security and performance best practices.

Core Concept Analysis

The WordPress Application Flow

┌──────────────────────────────────────────┐
│              Web Server (Nginx/Apache)   │
│ Receives request: example.com/my-post    │
└──────────────────────────────────────────┘
                     │
                     ▼
┌──────────────────────────────────────────┐
│              WordPress Core (index.php)  │
│ Sets up constants, loads wp-config.php    │
└──────────────────────────────────────────┘
                     │
                     ▼
┌──────────────────────────────────────────┐
│            wp-config.php                 │
│ DB credentials, salts, debug settings    │
└──────────────────────────────────────────┘
                     │
                     ▼
┌──────────────────────────────────────────┐
│             wp-settings.php              │
│ Loads plugins, active theme's functions.php│
│ Fires 'init' action hook                  │
└──────────────────────────────────────────┘
                     │
                     ▼
┌──────────────────────────────────────────┐
│               WP Class ($wp)             │
│ Parses request, sets up query variables  │
└──────────────────────────────────────────┘
                     │
                     ▼
┌──────────────────────────────────────────┐
│          Template Hierarchy              │
│ Selects the correct template file        │
│ (e.g., single.php, page.php, index.php)  │
└──────────────────────────────────────────┘
                     │
                     ▼
┌──────────────────────────────────────────┐
│                  The Loop                │
│ while(have_posts()): the_post();         │
│ Renders content using template tags      │
└──────────────────────────────────────────┘

Key Concepts Explained

  1. Hooks: Actions and Filters: The foundation of WordPress extensibility.
    • Actions (do_action, add_action): Inject custom code at specific points (e.g., init, wp_head). They don’t return anything.
    • Filters (apply_filters, add_filter): Modify data before it’s used or displayed (e.g., the_content, the_title). They must return a value.
  2. Database Schema: A few key tables run the show.
    • wp_posts: Stores posts, pages, menu items, attachments, and custom post types. The post_type column is key.
    • wp_postmeta: Key/value store for additional data related to posts (e.g., custom fields).
    • wp_options: Key/value store for site-wide settings.
    • wp_users & wp_usermeta: For user accounts and their associated data.
    • wp_terms, wp_term_taxonomy, wp_term_relationships: Manages categories, tags, and custom taxonomies.
  3. Template Hierarchy: How WordPress chooses which theme file to render for a given request. It’s a cascade, looking for the most specific template file first and falling back to more general ones. (e.g., single-post-slug.php -> single-post.php -> single.php -> singular.php -> index.php).

  4. The Block Editor (Gutenberg): The modern content editor built with React.
    • Everything is a block: Paragraphs, images, headings, and custom components.
    • block.json: A metadata file that defines a block’s properties.
    • JavaScript (ESNext/React): Used to define the block’s behavior in the editor (edit function) and its frontend output (save function).

Project List

The following 12 projects will guide you from a WordPress user to a capable WordPress developer.


Project 1: Manual Install & Debug Environment

  • File: LEARN_WORDPRESS_DEEP_DIVE.md
  • Main Programming Language: PHP
  • Alternative Programming Languages: N/A
  • Coolness Level: Level 1: Pure Corporate Snoozefest
  • Business Potential: 1. The “Resume Gold”
  • Difficulty: Level 1: Beginner
  • Knowledge Area: Server Configuration / WordPress Core
  • Software or Tool: MySQL, Apache/Nginx, PHP
  • Main Book: “WordPress For Dummies” by Lisa Sabin-Wilson (for initial setup concepts)

What you’ll build: A local WordPress site installed completely by hand—no automated installers. You’ll create the database, user, and wp-config.php file manually.

Why it teaches WordPress: One-click installers hide what WordPress actually needs to run. A manual install forces you to understand the core requirements: a web server, a database, and the correct configuration to link them. It’s the first step from user to owner.

Core challenges you’ll face:

  • Creating a MySQL database and user → maps to understanding WordPress’s data store
  • Configuring wp-config.php from the sample file → maps to learning about database credentials, salts, and table prefixes
  • Enabling debug mode (WP_DEBUG) → maps to learning how to surface errors and notices, a critical development practice
  • Fixing common connection errors → maps to troubleshooting skills for database or server issues

Key Concepts:

Difficulty: Beginner Time estimate: 2-3 hours Prerequisites: Access to a local server environment (like XAMPP, MAMP, or Local).

Real world outcome: A working, locally hosted WordPress site. More importantly, you’ll have a wp-config.php file with debugging enabled, which will display PHP warnings and notices, making future development much easier.

// In your wp-config.php
define( 'WP_DEBUG', true );
define( 'WP_DEBUG_LOG', true ); // Errors will be saved to a debug.log file
define( 'WP_DEBUG_DISPLAY', true ); // Errors will be shown in the HTML

Learning milestones:

  1. Successful installation → You understand the basic components of a WordPress site.
  2. Enabling debug mode → You know how to get feedback and error information.
  3. Troubleshooting a “database connection error” → You’ve learned the most common failure point.

Project 2: Build a Classic Theme from Scratch

  • File: LEARN_WORDPRESS_DEEP_DIVE.md
  • Main Programming Language: PHP, CSS
  • Alternative Programming Languages: N/A
  • Coolness Level: Level 2: Practical but Forgettable
  • Business Potential: 1. The “Resume Gold”
  • Difficulty: Level 2: Intermediate
  • Knowledge Area: Theming / Template Hierarchy
  • Software or Tool: A code editor
  • Main Book: “Professional WordPress Plugin Development” by Brad Williams, Ozh Richard, and Justin Tadlock (Concepts on hooks apply here)

What you’ll build: A bare-bones, classic WordPress theme. You will create the essential files (style.css, index.php, header.php, footer.php, functions.php) to render posts and pages.

Why it teaches WordPress: Building a theme from an empty folder is the only way to truly learn the Template Hierarchy. You’ll understand how WordPress selects files to render content and how The Loop is used to display posts.

Core challenges you’ll face:

  • Creating style.css with the required header → maps to how WordPress identifies a theme
  • Implementing The Loop in index.php → maps to the core mechanism for querying and displaying posts
  • Separating header.php and footer.php → maps to creating reusable template parts with get_header() and get_footer()
  • Enqueuing stylesheets correctly in functions.php → maps to the proper way to add assets using the wp_enqueue_scripts hook

Key Concepts:

Difficulty: Intermediate Time estimate: 1-2 weekends Prerequisites: Project 1, basic HTML/CSS, basic PHP.

Real world outcome: A simple but functional theme that correctly displays your blog posts and pages. It won’t be beautiful, but it will work, and you will understand why it works. You’ll see your posts listed on the homepage and be able to click through to a single post view (even if it uses index.php for everything).

Implementation Hints: Start with only two files:

  1. style.css: Add the theme name comment at the top.
    /*
    Theme Name: My First Theme
    */
    
  2. index.php: Add a basic HTML structure and The Loop.
    <?php get_header(); ?>
    <h1>My Blog</h1>
    <?php if ( have_posts() ) : ?>
        <?php while ( have_posts() ) : the_post(); ?>
            <h2><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></h2>
            <?php the_content(); ?>
        <?php endwhile; ?>
    <?php endif; ?>
    <?php get_footer(); ?>
    

    Activate it in the admin panel and see what happens. Then, slowly create header.php, footer.php, single.php, etc., to see how the display changes.

Learning milestones:

  1. Theme appears in the admin panel → You understand theme identification.
  2. Posts display on the front page → You’ve implemented The Loop correctly.
  3. Single posts display correctly (using single.php) → You understand template hierarchy.
  4. CSS is applied correctly → You’ve mastered asset enqueuing.

Project 3: A Simple “Post Metadata” Plugin

  • File: LEARN_WORDPRESS_DEEP_DIVE.md
  • Main Programming Language: PHP
  • Alternative Programming Languages: N/A
  • Coolness Level: Level 3: Genuinely Clever
  • Business Potential: 1. The “Resume Gold”
  • Difficulty: Level 2: Intermediate
  • Knowledge Area: Plugin Development / Hooks
  • Software or Tool: Code editor
  • Main Book: “Professional WordPress Plugin Development” by Brad Williams et al.

What you’ll build: A plugin that, when active, displays a list of all custom fields (post meta) at the end of each single post.

Why it teaches WordPress: This is the perfect introduction to plugins and hooks. You’ll learn how to create a file that WordPress recognizes as a plugin, and then use a filter hook (the_content) to modify data before it’s displayed to the user.

Core challenges you’ll face:

  • Creating the plugin header → maps to how WordPress identifies and activates a plugin
  • Hooking into the_content filter → maps to understanding how to modify post content
  • Getting post metadata (get_post_meta) → maps to interacting with the wp_postmeta database table via WordPress functions
  • Ensuring the code only runs on single posts → maps to using conditional tags like is_single()

Key Concepts:

Difficulty: Intermediate Time estimate: A weekend Prerequisites: Project 1, understanding of PHP functions.

Real world outcome: After activating the plugin and adding some custom fields to a post, you will see a simple formatted list of those key/value pairs automatically appear at the bottom of that post on your website.

Implementation Hints: Your main plugin file will have a structure like this:

<?php
/*
Plugin Name: My Metadata Viewer
*/

// This is the core function that modifies the content
function my_display_post_meta( $content ) {
    // Only do this on single post pages
    if ( is_single() && in_the_loop() && is_main_query() ) {
        $all_meta = get_post_meta( get_the_ID() );
        $meta_html = '<h3>Post Metadata:</h3><ul>';

        foreach ($all_meta as $key => $value) {
            // Simple formatting
            $meta_html .= '<li><strong>' . esc_html($key) . ':</strong> ' . esc_html($value[0]) . '</li>';
        }

        $meta_html .= '</ul>';
        return $content . $meta_html; // Append our HTML to the original content
    }
    return $content; // Return original content if not a single post
}

// This line is where the magic happens
add_filter( 'the_content', 'my_display_post_meta' );

This demonstrates the entire lifecycle: hooking, checking context, getting data, and filtering output.

Learning milestones:

  1. Plugin activates without errors → You understand the plugin file structure.
  2. Metadata appears on posts → Your add_filter call is working.
  3. Metadata does not appear on pages or archives → You’ve successfully used conditional logic.

Project 4: Custom Post Type Plugin for “Projects”

  • File: LEARN_WORDPRESS_DEEP_DIVE.md
  • Main Programming Language: PHP
  • Alternative Programming Languages: N/A
  • Coolness Level: Level 3: Genuinely Clever
  • Business Potential: 2. The “Micro-SaaS / Pro Tool”
  • Difficulty: Level 2: Intermediate
  • Knowledge Area: Data Architecture / Plugin Development
  • Software or Tool: Code Editor
  • Main Book: “Professional WordPress Development” by Hal Stern, David Damstra, and Brad Williams

What you’ll build: A plugin to create a “Projects” section in your WordPress admin, separate from Posts and Pages. This will include a custom post type and a custom taxonomy (like “Service Type”).

Why it teaches WordPress: This breaks you out of the “posts and pages” mindset. You learn how to model different types of content, which is the foundation of using WordPress as a true CMS.

Core challenges you’ll face:

  • Registering a custom post type (register_post_type) → maps to understanding how to create new content types
  • Registering a custom taxonomy (register_taxonomy) → maps to creating custom categorization systems like tags or categories
  • Hooking into init → maps to the correct action hook to register post types and taxonomies
  • Flushing rewrite rules → maps to solving the common “404 error” problem after creating a new post type

Key Concepts:

Difficulty: Intermediate Time estimate: A weekend Prerequisites: Project 3, understanding of PHP arrays.

Real world outcome: A new “Projects” menu item will appear in your WordPress admin sidebar. You can add new projects, categorize them by “Service Type,” and view them on the frontend at a URL like example.com/projects/my-cool-project.

Implementation Hints: Hook your registration function into the init action.

function my_cpt_plugin_register() {
    // Define the arguments for the "Projects" post type
    $project_args = [
        'public' => true,
        'label'  => 'Projects',
        'supports' => ['title', 'editor', 'thumbnail'],
        'rewrite' => ['slug' => 'projects'],
    ];
    register_post_type( 'project', $project_args );

    // Define arguments for the "Service Type" taxonomy
    $taxonomy_args = [
        'public' => true,
        'label' => 'Service Types',
        'rewrite' => ['slug' => 'service-type'],
    ];
    register_taxonomy( 'service_type', 'project', $taxonomy_args );
}
add_action( 'init', 'my_cpt_plugin_register' );

// It is critical to flush rewrite rules on plugin activation/deactivation
// to avoid 404 errors on your new CPT archive/single pages.
function my_cpt_plugin_rewrite_flush() {
    my_cpt_plugin_register();
    flush_rewrite_rules();
}
register_activation_hook( __FILE__, 'my_cpt_plugin_rewrite_flush' );

You will also need to create templates in your theme (archive-project.php and single-project.php) to display them correctly.

Learning milestones:

  1. “Projects” menu appears in admin → Your register_post_type function is working.
  2. You can create a new Project and it saves → The CPT is correctly configured.
  3. You can view the project on the frontend without a 404 → Rewrite rules are working.
  4. You can assign and view “Service Types” → Your custom taxonomy is correctly linked.

Project 5: Create a Custom Gutenberg Block

  • File: LEARN_WORDPRESS_DEEP_DIVE.md
  • Main Programming Language: JavaScript (React/JSX)
  • Alternative Programming Languages: PHP (for server-side rendering)
  • Coolness Level: Level 4: Hardcore Tech Flex
  • Business Potential: 2. The “Micro-SaaS / Pro Tool”
  • Difficulty: Level 3: Advanced
  • Knowledge Area: Modern Frontend / Block Editor
  • Software or Tool: Node.js/npm, @wordpress/create-block
  • Main Book: “Gutenberg Bible” by Ahmad Awais and associates

What you’ll build: A custom “Call to Action” block with an editable title, description, and a button with a configurable link.

Why it teaches WordPress: This is the modern way to develop for WordPress. It forces you to learn the JavaScript-heavy, React-based architecture of the new editor and how the build process works.

Core challenges you’ll face:

  • Setting up a JavaScript build environment → maps to using npm and the @wordpress/scripts package
  • Understanding block.json → maps to the central file for block registration and metadata
  • Writing the edit function in JSX → maps to creating the block’s appearance and behavior within the editor
  • Writing the save function → maps to defining the static HTML that gets saved to the database
  • Managing attributes → maps to storing and updating the block’s content (the title, description, etc.)

Key Concepts:

Difficulty: Advanced Time estimate: 1-2 weeks Prerequisites: Project 3, solid understanding of JavaScript (React is helpful but not strictly required to start).

Real world outcome: You will be able to go to the block inserter in the post editor, find your “Call to Action” block, and add it to a post. You’ll be able to type a title and description directly into the editor and set the button URL in the block’s sidebar settings.

Implementation Hints: The easiest way to start is with the official scaffolding tool:

npx @wordpress/create-block call-to-action-block

This will create a new plugin with a folder call-to-action-block containing all the necessary files. Your main focus will be on src/block.json, src/edit.js, and src/save.js.

edit.js (Simplified):

import { useBlockProps, RichText } from '@wordpress/block-editor';

export default function Edit( { attributes, setAttributes } ) {
    return (
        <div { ...useBlockProps() }>
            <RichText
                tagName="h2"
                value={ attributes.title }
                onChange={ ( title ) => setAttributes( { title } ) }
                placeholder="Enter title..."
            />
            {/* ... more fields for description and button ... */}
        </div>
    );
}

save.js (Simplified):

import { useBlockProps, RichText } from '@wordpress/block-editor';

export default function save( { attributes } ) {
    return (
        <div { ...useBlockProps.save() }>
            <RichText.Content tagName="h2" value={ attributes.title } />
            {/* ... render the rest of the block's content ... */}
        </div>
    );
}

Learning milestones:

  1. Block appears in the inserter → Your block is correctly registered.
  2. You can edit text in the editor → Your edit function and attributes are working.
  3. Content saves and displays on the frontend → Your save function is outputting correct HTML.
  4. Block settings appear in the sidebar → You’ve learned to use InspectorControls.

Project 6: Extend the Customizer

  • File: LEARN_WORDPRESS_DEEP_DIVE.md
  • Main Programming Language: PHP, JavaScript
  • Alternative Programming Languages: N/A
  • Coolness Level: Level 2: Practical but Forgettable
  • Business Potential: 1. The “Resume Gold”
  • Difficulty: Level 2: Intermediate
  • Knowledge Area: Theming / APIs
  • Software or Tool: Code Editor
  • Main Book: “WordPress Theme Development” (various authors, online resources are better here)

What you’ll build: Extend the theme from Project 2 by adding options to the WordPress Customizer. You’ll add a field for a footer copyright message and a color picker for the site’s background.

Why it teaches WordPress: It teaches you how to make themes configurable for end-users in a standard, built-in way. You’ll learn about the Customizer API and how to live-preview changes with JavaScript.

Core challenges you’ll face:

  • Hooking into customize_register → maps to the entry point for adding new Customizer options
  • Adding sections, settings, and controls → maps to the three core components of a Customizer option
  • Outputting the saved setting in your theme → maps to using get_theme_mod() to retrieve the value
  • (Advanced) Implementing a live preview → maps to using the Customizer’s JavaScript API to reflect changes without a full page reload

Key Concepts:

Difficulty: Intermediate Time estimate: A weekend Prerequisites: Project 2.

Real world outcome: In the “Appearance > Customize” screen, you will have a new section. Inside, you’ll find a text box to change the footer copyright text and a color wheel to change the background color. When you change them, the preview on the right will update instantly.

Implementation Hints: In your theme’s functions.php:

function mytheme_customize_register( $wp_customize ) {
    // Add a new section for our theme options
    $wp_customize->add_section( 'mytheme_options', [
        'title' => __( 'Theme Options', 'mytheme' ),
        'priority' => 30,
    ]);

    // Add setting for footer text
    $wp_customize->add_setting( 'footer_copyright_text', [
        'default' => 'Copyright 2025',
        'sanitize_callback' => 'sanitize_text_field',
    ]);

    // Add control for footer text
    $wp_customize->add_control( 'footer_copyright_text', [
        'label' => __( 'Footer Copyright', 'mytheme' ),
        'section' => 'mytheme_options',
        'type' => 'text',
    ]);
}
add_action( 'customize_register', 'mytheme_customize_register' );

Then, in your footer.php:

<footer>
    <p><?php echo esc_html( get_theme_mod( 'footer_copyright_text', 'Default Copyright' ) ); ?></p>
</footer>

Learning milestones:

  1. New section appears in the Customizer → Your customize_register hook is working.
  2. Changing the footer text and saving works → You’ve correctly implemented a setting and control, and are retrieving it with get_theme_mod.
  3. The default value appears correctly → You understand the default parameter in add_setting.

Project 7: Create a Custom WP-CLI Command

  • File: LEARN_WORDPRESS_DEEP_DIVE.md
  • Main Programming Language: PHP
  • Alternative Programming Languages: N/A
  • Coolness Level: Level 4: Hardcore Tech Flex
  • Business Potential: 3. The “Service & Support” Model
  • Difficulty: Level 3: Advanced
  • Knowledge Area: Automation / Command Line
  • Software or Tool: WP-CLI
  • Main Book: WP-CLI Handbook

What you’ll build: A custom WP-CLI command, wp post-stats, that prints a summary of the site’s content (e.g., total number of posts, pages, and their publication status).

Why it teaches WordPress: It takes you beyond the web UI and into the world of automation and server-side management. You learn how to perform WordPress actions programmatically, which is essential for large-scale sites and professional workflows.

Core challenges you’ll face:

  • Structuring a class for a WP-CLI command → maps to the required format for custom commands
  • Registering the command with WP_CLI::add_command → maps to making your command available to WP-CLI
  • Fetching data using WordPress functions (wp_count_posts) → maps to reusing WordPress core functions in a non-web context
  • Formatting output for the command line → maps to using WP_CLI::success, WP_CLI::error, and WP_CLI\Utils\format_items

Key Concepts:

Difficulty: Advanced Time estimate: A weekend Prerequisites: Project 3, comfort with the command line.

Real world outcome: From your terminal, in your WordPress directory, you can run wp post-stats and see neatly formatted output like:

$ wp post-stats
+----------+-------+-----------+---------+
| type     | total | published | pending |
+----------+-------+-----------+---------+
| post     | 50    | 45        | 5       |
| page     | 10    | 10        | 0       |
| project  | 25    | 20        | 5       |
+----------+-------+-----------+---------+
Success: Content statistics retrieved.

Implementation Hints: You can add this to a plugin or your theme’s functions.php (for simplicity).

if ( defined( 'WP_CLI' ) && WP_CLI ) {

    class Post_Stats_Command {
        /**
         * Prints a summary of post type counts.
         */
        public function __invoke() {
            $post_types = get_post_types( ['public' => true], 'objects' );
            $output_items = [];

            foreach ( $post_types as $post_type ) {
                $counts = wp_count_posts( $post_type->name );
                $output_items[] = [
                    'type'      => $post_type->name,
                    'total'     => array_sum( (array) $counts ),
                    'published' => $counts->publish,
                    'pending'   => $counts->pending,
                ];
            }
            
            WP_CLI\Utils\format_items( 'table', $output_items, ['type', 'total', 'published', 'pending'] );
            WP_CLI::success( 'Content statistics retrieved.' );
        }
    }

    WP_CLI::add_command( 'post-stats', 'Post_Stats_Command' );
}

Learning milestones:

  1. Command appears in wp help → Your command is correctly registered.
  2. Running wp post-stats outputs data → Your command’s main logic is executing.
  3. Output is a formatted table → You’ve used the WP-CLI utility functions correctly.

Project 8: Build a Headless Frontend for WordPress

  • File: LEARN_WORDPRESS_DEEP_DIVE.md
  • Main Programming Language: JavaScript (React/Next.js or Vue/Nuxt)
  • Alternative Programming Languages: Svelte/SvelteKit
  • Coolness Level: Level 4: Hardcore Tech Flex
  • Business Potential: 5. The “Industry Disruptor”
  • Difficulty: Level 3: Advanced
  • Knowledge Area: Headless CMS / JAMstack / APIs
  • Software or Tool: Node.js/npm, a JS framework
  • Main Book: “The JAMstack” by Mathias Biilmann & Phil Hawksworth

What you’ll build: A simple, fast, and modern blog frontend using a JavaScript framework like Next.js that pulls all its content from your WordPress site via the REST API.

Why it teaches WordPress: This project teaches you to decouple WordPress’s backend from its frontend. You’ll learn to treat WordPress as a pure content repository and API, a hugely popular and powerful modern architecture.

Core challenges you’ll face:

  • Fetching data from the WP REST API → maps to making fetch requests to /wp-json/wp/v2/posts
  • Handling routes in the JS framework → maps to mapping /posts/[slug] to a WordPress post
  • Rendering block content → maps to the challenge of safely rendering the HTML from Gutenberg blocks (dangerouslySetInnerHTML)
  • Handling featured images and metadata → maps to making additional API calls to get related data

Key Concepts:

Difficulty: Advanced Time estimate: 2-3 weeks Prerequisites: Project 1, Strong JavaScript knowledge, familiarity with at least one JS framework.

Real world outcome: You will have two separate applications: your WordPress site (acting as the CMS) and a blazing-fast Node.js-based frontend. The frontend will display your posts and pages, but it will be a completely separate codebase, which you can deploy to modern hosting platforms like Vercel or Netlify.

Implementation Hints: Use fetch to get your posts. In a Next.js page component:

// pages/index.js
export async function getStaticProps() {
    const res = await fetch('https://your-wp-site.com/wp-json/wp/v2/posts');
    const posts = await res.json();
    return { props: { posts } };
}

function HomePage({ posts }) {
    return (
        <div>
            <h1>My Blog</h1>
            <ul>
                {posts.map(post => (
                    <li key={post.id}>
                        <a href={`/posts/${post.slug}`}>{post.title.rendered}</a>
                    </li>
                ))}
            </ul>
        </div>
    );
}

export default HomePage;

Learning milestones:

  1. Homepage displays a list of post titles → You are successfully fetching data from the /posts endpoint.
  2. Clicking a title leads to a single post page with content → You have dynamic routing and are rendering post content.
  3. Featured images are displayed → You’re handling linked data from the API (might need ?_embed).

Project 9: Create a Custom REST API Endpoint

  • File: LEARN_WORDPRESS_DEEP_DIVE.md
  • Main Programming Language: PHP
  • Alternative Programming Languages: N/A
  • Coolness Level: Level 4: Hardcore Tech Flex
  • Business Potential: 4. The “Open Core” Infrastructure
  • Difficulty: Level 3: Advanced
  • Knowledge Area: API Development / Backend
  • Software or Tool: Code Editor, Postman (or similar API client)
  • Main Book: WordPress REST API Handbook

What you’ll build: A custom REST API endpoint /wp-json/my-stats/v1/counts that returns a JSON object with the total count of all public posts, pages, and comments.

Why it teaches WordPress: While WordPress provides many default endpoints, true power comes from creating your own. This teaches you how to expose custom data and functionality from your WordPress site to any external application.

Core challenges you’ll face:

  • Hooking into rest_api_init → maps to the correct entry point for registering custom routes
  • Using register_rest_route → maps to defining your endpoint’s namespace, route, methods, and callback function
  • Writing the callback function → maps to gathering the data and returning it in the correct format (WP_REST_Response)
  • Permissions and sanitization → maps to securing your endpoint with a permission_callback

Key Concepts:

Difficulty: Advanced Time estimate: A weekend Prerequisites: Project 3.

Real world outcome: You can navigate to https://your-site.com/wp-json/my-stats/v1/counts in your browser (or API client) and receive a clean JSON response, something like:

{
  "posts": 152,
  "pages": 15,
  "comments": 489
}

Implementation Hints: Add this to a plugin file:

function my_stats_register_routes() {
    register_rest_route( 'my-stats/v1', '/counts', [
        'methods' => 'GET',
        'callback' => 'my_stats_get_counts',
        'permission_callback' => '__return_true' // Publicly accessible
    ]);
}
add_action( 'rest_api_init', 'my_stats_register_routes' );

function my_stats_get_counts() {
    $post_count = wp_count_posts('post')->publish;
    $page_count = wp_count_posts('page')->publish;
    $comment_count = wp_count_comments()->total_comments;

    $data = [
        'posts' => (int) $post_count,
        'pages' => (int) $page_count,
        'comments' => (int) $comment_count,
    ];

    return new WP_REST_Response( $data, 200 );
}

Learning milestones:

  1. Endpoint is visible at the index (/wp-json/) → Your route is registered.
  2. Accessing the endpoint returns the correct JSON data → Your callback function is working.
  3. Implementing a basic permission check → You understand how to secure an endpoint.

Project 10: Convert a Classic Theme to a Block Theme

  • File: LEARN_WORDPRESS_DEEP_DIVE.md
  • Main Programming Language: JSON, HTML
  • Alternative Programming Languages: PHP (minimal)
  • Coolness Level: Level 3: Genuinely Clever
  • Business Potential: 1. The “Resume Gold”
  • Difficulty: Level 3: Advanced
  • Knowledge Area: Modern Theming / Full Site Editing (FSE)
  • Software or Tool: Code Editor, WordPress Site Editor
  • Main Book: “Block Theme Handbook” (Official WordPress Docs)

What you’ll build: Take the classic theme you built in Project 2 and convert it into a modern, block-based theme compatible with the Full Site Editor.

Why it teaches WordPress: This project throws you into the future of WordPress theming. You’ll learn how to replace PHP-based templates with HTML block templates and consolidate all styling and settings into a single theme.json file.

Core challenges you’ll face:

  • Creating a theme.json file → maps to defining global styles, settings, and layout
  • Replacing PHP templates with HTML block templates → maps to creating templates/index.html and parts/header.html
  • Understanding block markup → maps to the comment-based syntax WordPress uses to define blocks in HTML files
  • Removing legacy code → maps to deleting header.php, footer.php, and enqueuing functions from functions.php

Key Concepts:

Difficulty: Advanced Time estimate: 1-2 weeks Prerequisites: Project 2 and Project 5.

Real world outcome: Your theme will now have “Editor (beta)” under the Appearance menu. You’ll be able to visually edit your header, footer, and page templates using the block editor interface, with styles and fonts controlled by your theme.json file.

Implementation Hints:

  1. In your theme folder, delete header.php, footer.php, and single.php.
  2. Create a theme.json file. Start small:
    {
      "version": 2,
      "settings": {
        "layout": {
          "contentSize": "800px",
          "wideSize": "1200px"
        }
      },
      "styles": {
        "typography": {
          "fontFamily": "serif"
        }
      }
    }
    
  3. Create a templates folder. Inside, create index.html:
    <!-- wp:template-part {"slug":"header","tagName":"header"} /-->
    
    <!-- wp:query {"queryId":1,"query":{"perPage":10,"pages":0,"offset":0,"postType":"post","order":"desc","orderBy":"date","author":"","search":"","exclude":[],"sticky":"","inherit":true},"tagName":"main"} -->
    <main class="wp-block-query">
      <!-- wp:post-template -->
        <!-- wp:post-title {"isLink":true} /-->
        <!-- wp:post-content /-->
      <!-- /wp:post-template -->
    </main>
    <!-- /wp:query -->
    
    <!-- wp:template-part {"slug":"footer","tagName":"footer"} /-->
    
  4. Create a parts folder for reusable template parts like header.html.

Learning milestones:

  1. Site renders using index.html → You have successfully created a basic block template.
  2. Global styles from theme.json are applied → You understand how theme.json controls the site’s look and feel.
  3. You can edit the header in the Site Editor → You have successfully created and used a template part.

Project 11: A Basic Caching Plugin

  • File: LEARN_WORDPRESS_DEEP_DIVE.md
  • Main Programming Language: PHP
  • Alternative Programming Languages: N/A
  • Coolness Level: Level 4: Hardcore Tech Flex
  • Business Potential: 3. The “Service & Support” Model
  • Difficulty: Level 3: Advanced
  • Knowledge Area: Performance / Filesystem
  • Software or Tool: Code Editor
  • Main Book: “High Performance WordPress” by Chris Scott, David M. Long, and Steve Souders

What you’ll build: A simple file-based caching plugin. It will intercept the page generation process, save the final HTML of a single post to a static file, and serve that file on subsequent visits to bypass PHP and database execution.

Why it teaches WordPress: This dives deep into performance and the WordPress execution order. You learn to control the output buffer and understand the trade-offs of caching, a critical skill for any professional developer.

Core challenges you’ll face:

  • PHP Output Buffering (ob_start, ob_get_contents) → maps to capturing the generated HTML before it’s sent to the browser
  • Creating a cache directory and files → maps to interacting with the server filesystem using PHP
  • Serving the cached file → maps to hooking in early enough in the WordPress load to serve the static file and exit
  • Cache invalidation → maps to deleting the cached file when a post is updated (save_post hook)

Key Concepts:

Difficulty: Advanced Time estimate: 1-2 weeks Prerequisites: Project 3, solid PHP knowledge.

Real world outcome: When you load a post for the first time, it will be generated by WordPress as normal. If you reload, the page will load almost instantly. You can inspect the page source and see a comment at the bottom like <!-- Cached on [timestamp] -->. Updating the post in the admin will automatically clear the cache for that page.

Implementation Hints:

// Hook in early to check for a cached file
add_action('template_redirect', 'my_caching_plugin_serve_cache', 0);

function my_caching_plugin_serve_cache() {
    if (is_single()) {
        $cache_file = get_cache_filepath(get_the_ID());
        if (file_exists($cache_file)) {
            echo file_get_contents($cache_file);
            exit;
        }
    }
}

// Hook in to start buffering
add_action('template_redirect', 'my_caching_plugin_start_buffer', 1);
function my_caching_plugin_start_buffer() {
    if(is_single()){
        ob_start('my_caching_plugin_cache_output');
    }
}

// The callback for ob_start to save the content
function my_caching_plugin_cache_output($buffer) {
    $cache_file = get_cache_filepath(get_the_ID());
    file_put_contents($cache_file, $buffer . '<!-- Cached on ' . time() . ' -->');
    return $buffer;
}

// Hook to clear cache on post update
add_action('save_post', 'my_caching_plugin_clear_cache');
function my_caching_plugin_clear_cache($post_id) {
    $cache_file = get_cache_filepath($post_id);
    if (file_exists($cache_file)) {
        unlink($cache_file);
    }
}

// Helper function to define cache path
function get_cache_filepath($post_id) {
    $cache_dir = WP_CONTENT_DIR . '/my-cache/';
    if (!is_dir($cache_dir)) {
        mkdir($cache_dir);
    }
    return $cache_dir . $post_id . '.html';
}

Learning milestones:

  1. Cache files are created in the directory → Your output buffering and file-writing logic works.
  2. Subsequent page loads are faster → You are successfully serving from cache.
  3. Updating a post deletes the cache file → Your cache invalidation hook is working.

Project 12: Build a Small Membership Plugin

  • File: LEARN_WORDPRESS_DEEP_DIVE.md
  • Main Programming Language: PHP
  • Alternative Programming Languages: JavaScript (for any frontend interactions)
  • Coolness Level: Level 3: Genuinely Clever
  • Business Potential: 4. The “Open Core” Infrastructure
  • Difficulty: Level 4: Expert
  • Knowledge Area: User Roles / Access Control / E-commerce
  • Software or Tool: Code Editor
  • Main Book: “Professional WordPress Plugin Development” by Brad Williams et al.

What you’ll build: A plugin that restricts content to logged-in users. It will add a “Members Only” checkbox to the post editor. If checked, non-logged-in users trying to view the post will be shown a login form or a “Please subscribe” message instead of the content.

Why it teaches WordPress: This project ties together many advanced concepts: user roles/capabilities, metadata, and modifying the main query. It’s a practical, real-world application that forms the basis of many commercial plugins.

Core challenges you’ll face:

  • Adding a meta box to the post editor → maps to using add_meta_box to create custom fields
  • Saving post meta data → maps to using the save_post hook to store the value of your checkbox
  • Restricting content access → maps to using the_content filter or the template_redirect hook to check user status and intervene
  • Managing user capabilities → maps to checking if a user is logged in (is_user_logged_in()) or has a specific role

Key Concepts:

Difficulty: Expert Time estimate: 2-3 weeks Prerequisites: Projects 3, 4, and 9.

Real world outcome: You will have a fully functional content restriction system. You can mark certain posts as “Members Only.” Visitors will be blocked from seeing them, while logged-in users can view them normally.

Implementation Hints: You can restrict content in a few ways. The simplest is with the_content filter:

function my_membership_filter_content($content) {
    if (is_single()) {
        $is_members_only = get_post_meta(get_the_ID(), '_is_members_only', true);
        if ($is_members_only && !is_user_logged_in()) {
            return 'This content is for members only. Please log in or subscribe.';
        }
    }
    return $content;
}
add_filter('the_content', 'my_membership_filter_content');

A more robust method uses template_redirect to prevent the post from loading at all, redirecting to a login page instead.

Learning milestones:

  1. “Members Only” checkbox appears in the editor → Your meta box is working.
  2. The setting is saved when you update a post → Your save_post hook is correctly saving metadata.
  3. Content is blocked for logged-out users → Your content restriction logic is working.
  4. Content is visible for logged-in users → You are correctly differentiating between user states.

Summary

Project Main Programming Language
Manual Install & Debug Environment PHP
Build a Classic Theme from Scratch PHP, CSS
A Simple “Post Metadata” Plugin PHP
Custom Post Type Plugin for “Projects” PHP
Create a Custom Gutenberg Block JavaScript (React/JSX)
Extend the Customizer PHP, JavaScript
Create a Custom WP-CLI Command PHP
Build a Headless Frontend for WordPress JavaScript (React/Next.js or Vue/Nuxt)
Create a Custom REST API Endpoint PHP
Convert a Classic Theme to a Block Theme JSON, HTML
A Basic Caching Plugin PHP
Build a Small Membership Plugin PHP