August 14, 2025
5 min read
By Cojocaru David & ChatGPT

Table of Contents

This is a list of all the sections in this post. Click on any of them to jump to that section.

How to Create a Custom WordPress Plugin From Scratch in 2025 (Even If You’re Not a Coder)

Let’s be real at some point every site owner thinks, “Wouldn’t it be cool if WordPress just did this one extra thing?”
Maybe you want a tiny tweak to your checkout page. Maybe you need a custom dashboard widget for your team. Whatever it is, rolling your own plugin is way easier than juggling ten bloated add-ons that slow your site to a crawl.

So today, I’ll walk you through the exact steps I used last month to spin up a lightweight plugin for a bakery site. No fluff, no jargon just the stuff that matters.

Why roll your own plugin in 2025?

Pre-made plugins rock until they don’t. Here’s why a custom one often wins:

  • Exact fit - Need to stamp every PDF invoice with your cat’s paw-print logo? Done.
  • Zero bloat - You only ship the code you need, so your Core Web Vitals stay happy.
  • Theme freedom - Switch themes tomorrow and your feature still works.
  • Easy updates - No more crossing fingers when the “SEO Mega Pack Pro” plugin auto-updates and breaks everything.
  • Reuse & sell - Drop the same plugin on five client sites or list it on wordpress.org for that sweet passive income.

Quick gear check (2-minute prep)

Before we touch code, grab these:

  • Local playground - I use Local because it spins up a site faster than I make coffee.
  • Editor - VS Code with the PHP Intelephense extension. Trust me, the red squiggly lines save hours.
  • Basic PHP - If you can write a function and remember a semicolon, you’re golden.
  • File access - FTP or your host’s file manager works fine.

Ready? Cool. Let’s write some code.


Step 1 - Birth of a plugin folder

  1. Open wp-content/plugins.
  2. Create a new folder: my-first-plugin.
  3. Inside, create my-first-plugin.php.

That’s it. No ceremony, no confetti (yet).


Step 2 - Tell WordPress “Hey, I exist!”

Pop open my-first-plugin.php and drop in this header:

<?php
/**
 * Plugin Name: My First Plugin
 * Description: Adds a simple "Quote of the Day" widget to every post.
 * Version: 1.0.0
 * Author: You, the hero
 * License: GPL v2 or later
 */

Save. Refresh Plugins in your dashboard boom, it shows up. Don’t activate yet; we’ll add brains first.


Step 3 - Make it do something fun

Let’s print a random quote at the end of every post. Paste right below the header:

function mfp_add_quote($content) {
    $quotes = [
        "Life is short. Eat dessert first.",
        "Stressed is desserts spelled backwards.",
        "You can't buy happiness, but you can buy cake."
    ];
    $quote = $quotes[array_rand($quotes)];
    return $content . '<blockquote><em>' . esc_html($quote) . '</em></blockquote>';
}
add_filter('the_content', 'mfp_add_quote');

Activate the plugin, visit any post, and ta-da random bakery wisdom appears.


Step 4 - Level up with CSS & JS (without tears)

We want that quote to pop, so let’s add some style.

Create folders:

my-first-plugin/
├── css/
│ └── quote.css
└── js/
└── quote.js

quote.css:

.mfp-quote {
    border-left: 4px solid #ff6b6b;
    padding-left: 1rem;
    font-style: italic;
}

quote.js:

document.addEventListener('DOMContentLoaded', () => {
    const quote = document.querySelector('.mfp-quote');
    if (quote) quote.style.animation = 'fadeIn 0.5s';
});

Enqueue them properly:

function mfp_assets() {
    wp_enqueue_style('mfp-css', plugin_dir_url(__FILE__) . 'css/quote.css');
    wp_enqueue_script('mfp-js', plugin_dir_url(__FILE__) . 'js/quote.js', [], '1.0.0', true);
}
add_action('wp_enqueue_scripts', 'mfp_assets');

Now your quotes look snazzy and fade in smoothly. Neat, right?


Step 5 - Give users a settings page (because options rock)

Imagine the bakery owner wants to switch quotes herself. Let’s build a 5-minute settings page.

// 1. Create menu item
function mfp_admin_menu() {
    add_options_page(
        'Quote Settings',
        'Quote Settings',
        'manage_options',
        'mfp-settings',
        'mfp_settings_html'
    );
}
add_action('admin_menu', 'mfp_admin_menu');
 
// 2. The HTML form
function mfp_settings_html() { ?>
    <div class="wrap">
        <h1>Quote Settings</h1>
        <form method="post" action="options.php">
            <?php settings_fields('mfp_options'); ?>
            <label>Your custom quote:<br>
                <textarea name="mfp_custom_quote" rows="3" cols="50"><?= esc_textarea(get_option('mfp_custom_quote')); ?></textarea>
            </label>
            <?php submit_button(); ?>
        </form>
    </div>
<?php }
 
// 3. Register setting
function mfp_settings_init() {
    register_setting('mfp_options', 'mfp_custom_quote', 'sanitize_text_field');
}
add_action('admin_init', 'mfp_settings_init');

Now admins can paste their own quote. Update your earlier filter to check for this option:

function mfp_add_quote($content) {
    $custom = get_option('mfp_custom_quote');
    $quote  = $custom ?: "Default: Eat more cookies!";
    return $content . '<blockquote class="mfp-quote"><em>' . esc_html($quote) . '</em></blockquote>';
}

Step 6 - Test like your reputation depends on it (because it does)

Quick checklist:

  • Activate plugin.
  • View a post quote appears?
  • Change theme still there?
  • Turn on WP_DEBUG in wp-config.php to catch any screamy PHP notices.
  • Run Query Monitor to confirm no extra database hits.

Common rookie traps (and how to dodge them)

  • Forgetting prefixes - Always name functions mfp_, yourbrand_, etc. Collisions are real.
  • Echoing raw POST data - Sanitize everything with sanitize_text_field(), esc_html(), or nonces.
  • Inline scripts/styles - Use wp_enqueue_* so caching plugins don’t hate you.
  • Hard-coding paths - plugin_dir_url(__FILE__) is your friend.

Mini-FAQ (because someone will ask)

Q: Can I sell my plugin?
Yep. GPL license lets you sell it, but you must share code if someone asks.

Q: Will it break on the next WordPress update?
Unlikely if you stick to core hooks. I’ve got plugins running since 2019 that never blinked.

Q: Do I need OOP for bigger plugins?
Eventually. Start procedural, refactor to classes once you hit ~500 lines. Baby steps.


Your next 3 moves

  1. Ship v1.0 - Zip the folder, install on a live site, celebrate with actual cake.
  2. Add features - Maybe a Gutenberg block or REST endpoint next weekend?
  3. Share it - wordpress.org repo submission is free and the community loves fresh blood.

“The best code is the code you actually ship.”

#WordPressPlugin #CustomCode #PHPBasics #WebDevTips