DEV Community

Cover image for How to Create Your First WordPress Plugin: A Developer's Guide
Drazen Bebic
Drazen Bebic Subscriber

Posted on • Originally published at bebic.dev on

How to Create Your First WordPress Plugin: A Developer's Guide

Most WordPress developers start out by adding functionality in the form of code snippets to the functions.php file. While this is a completely valid approach for quick fixes and small additions, it is not scalable architecture. If you change your theme you lose all of your functionality.

The solution is to build a WordPress Plugin.

Plugins are the gold standard for extending WordPress functionality. They are good because they decouple your custom functionality from the visual presentation of your website. This guide will cover the core concepts of WordPress plugin development (including hooks and filters) by building a simple plugin from scratch.

What Actually is a WordPress Plugin?

At its very core, a WordPress plugin is just a simple PHP file located in the wp-content/plugins/ directory and follows a specific header format.

WordPress scans this directory when it loads and looks for these kind of files. When it finds files with valid metadata it registers them so that you can activate them through the admin dashboard. Once you activate them, the code found inside the plugin file(s) runs on every page load. This is your entry gateway to injecting logic, modifying database queries, or rendering custom HTML.

Prerequisites

You will need a local WordPress development environment. Luckily there are many great options to chose from. I recommend:

  • LocalWP (easiest to set up)
  • DDEV or Lando (if you prefer Docker-based workflows)

Since WordPress is written in PHP you will also need some basic PHP knowledge. You absolutely don't have to be a wizard, but you should understand functions, arrays, and basic logic flow.

Step 1: The Folder Structure

Let's get started! First thing that you need to do is to navigate to your wp-content/plugins/ directory and create a new folder named simple-read-time. Inside this folder, create a PHP file called simple-read-time.php.

It is best practice and convention to name your main PHP file the same as your folder to avoid confusion.

Step 2: The Plugin Header

I mentioned that WordPress scans for specific metadata inside plugin files. This metadata is in the form of a comment block which sits at the top of your plugin PHP file. It looks something like this:

<?php
/**
 * Plugin Name: Simple Read Time
 * Plugin URI: https://www.bebic.dev
 * Description: A lightweight plugin that calculates and displays the estimated reading time for blog posts.
 * Version: 1.0.0
 * Author: Drazen Bebic
 * Author URI: https://www.bebic.dev
 * License: GPL2
 */

// Prevent direct access to the file
if ( ! defined( 'ABSPATH' ) ) {
    exit;
}
Enter fullscreen mode Exit fullscreen mode

After doing this you should be able to see your plugin on the Plugins page:

The blank plugin is visible!

Developer Tip: Always include the defined('ABSPATH') check. This prevents malicious actors from executing your PHP file directly, bypassing the WordPress loading process.

Step 3: Understanding Hooks (Actions vs. Filters)

This is the part where it gets a bit tricky, but it's important to learn the difference because it is the very heart of WordPress development: hooks.

There's two types of hooks in WordPress:

  1. Actions (add_action): Events where you want to do something at a specific point (e.g., "When a post is saved, send an email"). Actions do not return data.
  2. Filters (add_filter): Events where you want to modify data before it is rendered or saved (e.g., "Take the post content, add a disclaimer to the bottom, and return it"). Filters must return data.

Our plugin is going to calculate the reading time of blog posts and prepend it to the their content. This process will modify the output, so we will need to use a filter.

Step 4: Building the Logic

Now that we know what to do, let's go ahead and do it. We will hook into the the_content. This hook controls the body text of any post.

Add this code to your plugin file:

/**
 * Calculates reading time and prepends it to the content.
 *
 * @param string $content The original post content.
 * @return string The modified content.
 */
function srt_add_reading_time( $content ) {
    // Only run on single posts, not on the homepage or archives
    if ( ! is_single() || ! is_main_query() ) {
        return $content;
    }

    // 1. Get the word count (strip HTML tags first)
    $word_count = str_word_count( strip_tags( $content ) );

    // 2. Average reading speed (roughly 200 words per minute)
    $reading_speed = 200;

    // 3. Calculate time
    $reading_time = ceil( $word_count / $reading_speed );

    // 4. Create the HTML wrapper
    $reading_time_html = sprintf(
        '<p class="srt-read-time"><small>%s min(s) read.</small></p>',
        $reading_time
    );

    // 5. Prepend to content and return
    return $reading_time_html . $content;
}

// Hook our function to the 'the_content' filter
add_filter( 'the_content', 'srt_add_reading_time' );
Enter fullscreen mode Exit fullscreen mode

What is happening here?

  1. The Check : is_single() ensures our filter only runs on single blog posts so that we don't accidentally clutter the homepage or blog feed.
  2. The Logic : We strip HTML tags from the body to get a pure word count, then divide this word count by 200 (average reading speed).
  3. The Return : We append our newly created HTML to the original $content so nothing gets lost. If we forgot to return $content at the end of the hook callback, the entire blog post would be blank. This would be bad.

Step 5: Adding Settings (Optional but Recommended)

Hardcoding the reading speed (200) is fine for a v1, but in a real-world scenario, you might want to make that customizable.

To do that, you would typically use the Settings API or the Options API to store a value in the wp_options table.

// Example of retrieving a saved option (defaults to 200 if not found)
$reading_speed = get_option( 'srt_reading_speed', 200 );
Enter fullscreen mode Exit fullscreen mode

For the sake of simplicity and keeping this guide digestible we will not cover the topic of creating a settings page. I will create a second part about this specific topic in my next blog post, so stay tuned!

Conclusion

Congratulations! You have built your very own WordPress plugin from scratch. Your end result will look a bit like this:

It's showing the read time, woo!

You can now move from "hacking the functions.php" to writing modular, reusable software. You know how to set up the plugin files and folders, create some hooks and inject logic into the WordPress rendering pipeline.

I have also created the full plugin avilable in the form of a GitHub gist right here.

Your next step? Maybe refactor your functions.php into one (or more?) plugins. You could also try to expand this plugin. Or perhaps stay tuned for the next part where I will explain how to utilize the Settings API and Options API of WordPress.

Top comments (0)