How to Create a WordPress Plugin From Scratch (My Complete 2026 Process)
No copy-paste theory — just the exact folder structure, hooks, and habits I use every time I build a plugin, broken down the way I wish someone had explained it to me three years ago.
Why You Can Trust This Guide
I've been writing WordPress plugins professionally since 2023. Every code block here is from a plugin I've actually shipped, not copied from a tutorial.
Every snippet is tested on a fresh WordPress 6.7+ install before publishing. If it's in this guide, it ran without errors on my screen first.
I revisit this guide whenever a WordPress core update changes how plugins should be built, so the steps stay current, not frozen in 2023.
What Is a WordPress Plugin, Really?
A WordPress plugin is a self-contained package of PHP code that adds, removes, or changes functionality on a site without editing WordPress's core files or your theme. It lives in its own folder inside wp-content/plugins/, and it talks to WordPress through a system of "hooks" instead of modifying anything directly.
That separation is the entire point. Your theme should control how a site looks. A plugin should control what a site does — a contact form, a custom post type, a tracking script, a shortcode. Mix the two, and the moment you switch themes, your functionality disappears with it. I've fixed that exact mistake for three different clients in my first year alone.
Before You Start: What You Actually Need
- A local WordPress install — I use Local by WP Engine, but XAMPP or MAMP work fine too.
- A code editor. VS Code with the PHP Intelephense extension is what I run daily.
- Basic PHP comfort: variables, functions, arrays, and if/else logic. You don't need to be advanced.
- Patience for one wrong character breaking an entire page. It happens to everyone, including me, still.
Set Up a Local WordPress Environment
Never write your first plugin directly on a live site. Install WordPress locally so you can break things freely — and you will break things. Local by WP Engine takes about five minutes: download it, click "Create a new site," and you'll have a working WordPress install with admin access on your own machine.
Testing locally means zero risk to a real site, and you can reset the whole environment in seconds if something breaks.
Create the Plugin Folder and Main File
Inside wp-content/plugins/, create a new folder. Name it exactly how you'll name your plugin — lowercase, hyphen-separated. I'll use my-first-plugin for this guide. Inside that folder, create one PHP file with the same name: my-first-plugin.php. That file is what WordPress will read.
my-first-plugin/
my-first-plugin.php
assets/
style.css
script.js
readme.txt
Write the Plugin Header
This is the part beginners skip and then wonder why their plugin never shows up in the dashboard. The header is a PHP comment block at the very top of your main file. WordPress parses it to know your plugin's name, version, and author — without it, your file is just inert PHP that WordPress ignores completely.
<?php
/**
* Plugin Name: My First Plugin
* Plugin URI: https://growwp.in
* Description: A simple starter plugin built step by step.
* Version: 1.0.0
* Author: Akash Vishwakarma
* Author URI: https://growwp.in
* License: GPL v2 or later
* Text Domain: my-first-plugin
*/
// Block direct access to this file.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
Without it, someone could open this file directly through a browser URL and trigger errors or expose file paths. It's one line, and I add it to every plugin I write.
Activate Your Plugin and Confirm It Works
Log in to wp-admin → Plugins. You should see "My First Plugin" listed with the description and author you wrote in the header. Click Activate. If nothing shows up, double-check that your header comment block sits at the very top of the file with no stray characters before <?php.
Make It Do Something — Your First Hook
This is where it stops being a blank file and becomes an actual plugin. WordPress runs on hooks: actions let you run code at a specific moment, and filters let you change data before WordPress uses it. Let's add a small credit line to the site footer using the wp_footer action.
<?php
function gwp_add_footer_credit() {
echo '<p class="gwp-credit">This site runs My First Plugin.</p>';
}
add_action( 'wp_footer', 'gwp_add_footer_credit' );
Save, refresh your site's front end, and you'll see the line appear right before the closing </body> tag. Notice the function name starts with gwp_ — that prefix matters, and I'll explain why in the mistakes section below.
Add a Shortcode
Shortcodes let users drop dynamic content into posts and pages using simple tags like [gwp_welcome]. They're one of the most common things clients ask for, so it's worth getting comfortable early.
<?php
function gwp_welcome_shortcode( $atts ) {
$atts = shortcode_atts( array(
'name' => 'Friend',
), $atts );
return '<div class="gwp-welcome">Welcome, ' . esc_html( $atts['name'] ) . '!</div>';
}
add_shortcode( 'gwp_welcome', 'gwp_welcome_shortcode' );
Now typing [gwp_welcome name="Akash"] into any post outputs "Welcome, Akash!" Notice I used esc_html() around the attribute — never echo a value straight from user input without escaping it first.
Enqueue CSS and JavaScript the Right Way
Never link a stylesheet or script with a raw <link> or <script> tag in a plugin. Use wp_enqueue_scripts so WordPress can manage loading order, avoid duplicate files, and let other plugins play nicely with yours.
<?php
function gwp_enqueue_assets() {
wp_enqueue_style(
'gwp-style',
plugin_dir_url( __FILE__ ) . 'assets/style.css',
array(),
'1.0.0'
);
wp_enqueue_script(
'gwp-script',
plugin_dir_url( __FILE__ ) . 'assets/script.js',
array(),
'1.0.0',
true
);
}
add_action( 'wp_enqueue_scripts', 'gwp_enqueue_assets' );
Build a Settings Page in wp-admin
Most real plugins need at least one settings screen. Use add_options_page() to register the page and the WordPress Settings API to handle saving — it gives you nonce protection and sanitization callbacks for free.
<?php
function gwp_register_settings_page() {
add_options_page(
'My First Plugin Settings',
'My First Plugin',
'manage_options',
'gwp-settings',
'gwp_render_settings_page'
);
}
add_action( 'admin_menu', 'gwp_register_settings_page' );
function gwp_render_settings_page() {
?>
<div class="wrap">
<h1>My First Plugin Settings</h1>
<form method="post" action="options.php">
<?php
settings_fields( 'gwp_settings_group' );
do_settings_sections( 'gwp-settings' );
submit_button();
?>
</form>
</div>
<?php
}
Secure Every Input — Nonces, Sanitize, Escape
This is the step that separates a plugin you can actually ship from one that gets flagged in a security review. Three habits, every single time: verify a nonce before processing a form, sanitize data coming in, and escape data going out.
<?php
if ( ! isset( $_POST['gwp_nonce'] ) || ! wp_verify_nonce( $_POST['gwp_nonce'], 'gwp_save_action' ) ) {
wp_die( 'Security check failed.' );
}
// Sanitize incoming data.
$safe_input = sanitize_text_field( wp_unslash( $_POST['gwp_name'] ) );
// Escape on the way out.
echo '<p>Hello, ' . esc_html( $safe_input ) . '</p>';
I've audited plugins that skipped this and got hit with form spam and stored XSS within weeks of launch. Three lines of security code now save days of cleanup later.
Add Activation and Deactivation Hooks
Use these to set up anything your plugin needs on activation — like a default option — and clean it up on deactivation, so you don't leave orphaned data in the database.
<?php
function gwp_on_activate() {
add_option( 'gwp_version', '1.0.0' );
}
register_activation_hook( __FILE__, 'gwp_on_activate' );
function gwp_on_deactivate() {
delete_option( 'gwp_version' );
}
register_deactivation_hook( __FILE__, 'gwp_on_deactivate' );
Make It Translation-Ready
Even if you only plan an English version today, wrapping visible text in translation functions costs nothing now and saves a full rewrite later. Use the Text Domain you set in your header.
<?php
echo esc_html__( 'Welcome to my plugin!', 'my-first-plugin' );
Debug Properly Using WP_DEBUG
Stop guessing why something isn't working. Open wp-config.php on your local install and turn on debugging so PHP errors show up in a log file instead of silently failing or breaking the page.
<?php
define( 'WP_DEBUG', true );
define( 'WP_DEBUG_LOG', true );
define( 'WP_DEBUG_DISPLAY', false );
With this on, every error gets logged to wp-content/debug.log instead of showing on screen — readable for you, invisible to visitors. I keep this on for every local build, no exceptions.
Package and Deploy Your Plugin
Once it's tested, zip the entire plugin folder — not just the PHP file — keeping the folder structure intact. That zip is what you upload through wp-admin → Plugins → Add New → Upload Plugin on any other site, or what you submit to the WordPress.org Plugin Directory for review if you're releasing it publicly.
Before zipping, I always do one final pass: deactivate and reactivate the plugin on a clean install, check the browser console for JS errors, and read through debug.log one more time. Five minutes here has saved me from shipping broken plugins more than once.
Download Free PDF Guide
Get the complete step-by-step WordPress plugin development guide as a beautifully formatted PDF. Perfect for offline reading and reference.
Mistakes I See Beginners Make (Because I Made Them Too)
- Skipping function name prefixes.
A function called
save_settings()will silently clash with another plugin using the same name and crash the site. Prefix everything — I usegwp_across all my plugins. - Forgetting the ABSPATH check.
Leaves the file directly accessible by URL, which can expose errors and server paths to anyone who finds it.
- Echoing raw user input.
Printing form data straight back to the page without
esc_html()opens the door to stored XSS — one of the most common reasons plugins fail security review. - Loading scripts on every page.
Enqueuing your CSS/JS globally instead of only where it's needed quietly slows down the entire site, not just the page that uses your plugin.
- Skipping readme.txt.
If you ever plan to submit to the WordPress.org repository, the readme.txt format is mandatory — writing it after the fact wastes more time than writing it as you go.
Frequently Asked Questions
wp-content/plugins/your-plugin-name/, with a main PHP file matching the folder name. WordPress scans this directory automatically and lists every valid plugin header it finds in the dashboard.Conclusion
Building your first WordPress plugin isn't about memorizing every function in the codex — it's about understanding four things: the folder structure, the plugin header, the hook system, and basic security habits. Once those click, every plugin you build afterward is just a variation on the same pattern.
Start small. Get the footer credit working, then the shortcode, then the settings page. Don't try to build a complex plugin on day one. If you'd rather start from working code than a blank file, grab the free PDF guide and build on top of it. If you get stuck, that's normal — I still hit walls on plugins three years in. Read the error, check the line it points to, and work backward from there.
