WordPress Plugin Development: The Complete Guide to Building Your First Plugin

Learn WordPress plugin development from scratch. This step-by-step tutorial covers plugin file structure, hooks (actions and filters), the Settings API, admin menus, shortcodes, enqueuing scripts, activation and deactivation hooks, security with nonces, and more - with a complete working plugin example throughout.

Building your first WordPress plugin from scratch is one of the most empowering skills a developer can have. Instead of relying on third-party plugins that may bloat your site, miss specific features, or introduce security vulnerabilities, you gain full control over functionality. This tutorial walks you through the complete process - from understanding plugin file structure to implementing settings pages, admin menus, shortcodes, security best practices, and proper activation and deactivation routines.

By the end of this guide, you will have a working WordPress plugin called Simple Note Manager - a real, functional plugin that demonstrates every core concept. You will understand how WordPress loads plugins, how hooks power the entire platform, and how to write code that integrates cleanly with WordPress without breaking anything else on the site.

What You Need Before You Start

WordPress plugin development requires a local development environment. You need PHP 7.4 or higher, a local WordPress installation (Local, XAMPP, or Docker all work), a code editor (VS Code is popular), and basic familiarity with PHP and WordPress theme files. You do not need to be a PHP expert - the concepts here are approachable for intermediate developers.

  • Local WordPress installation running on PHP 7.4+
  • Code editor with PHP syntax support
  • Basic PHP knowledge (functions, arrays, classes)
  • Access to your WordPress wp-content/plugins/ directory
  • WP_DEBUG enabled in wp-config.php for development

Enable debugging in your local wp-config.php by setting WP_DEBUG to true. This surfaces PHP notices and WordPress warnings that would otherwise be hidden, which is essential during development.


Understanding the Plugin File Structure

Every WordPress plugin lives in its own folder inside wp-content/plugins/. The folder name becomes the plugin’s identifier. For the Simple Note Manager plugin, the structure looks like this:

The main plugin file is always named to match the folder (or given a clear name). It contains the plugin header comment that WordPress reads to identify and display your plugin in the admin panel. This header is not optional - without it, WordPress will not recognize the file as a plugin.

The Plugin Header Comment

The plugin header is a specially formatted PHP comment block at the very top of your main plugin file. WordPress reads these fields and displays them on the Plugins screen in the admin area.

The Plugin Name and Version fields are the most important. The Text Domain is critical if you plan to make the plugin translatable - it must match your plugin folder name. Keep the version in sync with your changelog.


How WordPress Hooks Work: Actions and Filters

WordPress hooks are the mechanism that makes plugin development possible. Instead of modifying WordPress core files (which would be overwritten on every update), plugins hook into specific points in WordPress’s execution and add or modify behavior. There are two types of hooks: actions and filters.

Actions - Do Something at a Specific Point

An action hook tells WordPress “when you reach this point in execution, also run my function.” Common action hooks include init (early initialization), wp_enqueue_scripts (when scripts should be loaded), and admin_menu (when the admin menu is being built).

The add_action() function takes three main parameters: the hook name, your callback function, the priority (lower numbers run first, default is 10), and the number of arguments your callback accepts. Most of the time, you only need the first two.

Filters - Modify Data Before It’s Used

A filter hook intercepts data before WordPress uses it and lets you return a modified version. Your function must always return a value - returning nothing or null will break things. Common filter hooks include the_content (post content before display), the_title (post title), and wp_nav_menu_items (navigation menu items).

Never modify WordPress core files. Every hook your plugin needs already exists in WordPress. If you think you need to edit core, you need a different hook or a different approach.

Building the Plugin: Step-by-Step with Simple Note Manager

The Simple Note Manager plugin will let administrators add short notes from the WordPress admin, store them in the database, and display them on the frontend via a shortcode. This covers database interaction, admin UI, settings, shortcodes, and proper enqueuing all in one real example.

Step 1: Create the Plugin Folder and Main File

Create the folder wp-content/plugins/simple-note-manager/ and inside it create simple-note-manager.php with the plugin header and basic class structure.

Using a class prevents function name collisions with other plugins. The get_instance() singleton pattern ensures the class is only instantiated once. Calling Simple_Note_Manager::get_instance() at the bottom starts everything up cleanly.

Step 2: Activation and Deactivation Hooks

Activation hooks run when a site admin activates the plugin. This is where you create database tables, set default options, or check compatibility. Deactivation hooks run when a plugin is deactivated - use this to clean up temporary data or scheduled events, but NOT to delete user data (that belongs in uninstall).

Note the use of dbDelta() for table creation. This WordPress function intelligently compares the existing table structure with what you specify and only makes necessary changes - making it safe to call repeatedly during upgrades.

Step 3: Creating an Admin Menu

The admin_menu action hook is where you register menu pages. Use add_menu_page() for a top-level menu item or add_submenu_page() to nest under an existing menu like Settings or Tools.

The capability parameter (manage_options) controls who can see and access this page. Only users with that capability (administrators by default) will see the menu item. Always use appropriate capabilities - never show admin pages to subscriber-level users.

Step 4: The Settings API

The WordPress Settings API provides a standardized way to create, validate, and save plugin settings. It handles nonces, sanitization hooks, and the settings saved notification automatically. There are three key functions to understand: register_setting(), add_settings_section(), and add_settings_field().

The sanitize callback is critical - it runs before WordPress saves the value. Always sanitize and validate here. For text fields use sanitize_text_field(), for HTML use wp_kses_post(), for integers use intval(), and for URLs use esc_url_raw().


Shortcodes: Displaying Content in Posts and Pages

Shortcodes let users embed plugin output directly in post content or page content using a simple tag like [simple_notes]. The add_shortcode() function registers a tag and connects it to a callback function that returns (not echoes) HTML output.

Several important rules for shortcodes:

  • Always return output, never echo it directly
  • Use shortcode_atts() to merge default attributes with user-provided ones
  • Escape all output with esc_html(), esc_attr(), or esc_url()
  • Use output buffering (ob_start() / ob_get_clean()) when including template files

Enqueuing Scripts and Styles Correctly

Never add scripts or styles with raw <script> or <link> tags in WordPress. Use wp_enqueue_script() and wp_enqueue_style() - these handle dependency management, version caching, and proper placement in the document.

The wp_localize_script() function is the correct way to pass PHP data to JavaScript. It creates a JavaScript object in the global scope with the values you specify. This is how you pass AJAX URLs, nonces, and configuration values to your frontend scripts without hardcoding them.

Loading Scripts Only When Needed

Only load your plugin’s scripts and styles on pages where they are actually needed. Check post types, page templates, or shortcode presence before enqueuing. Loading scripts on every page of the site when they are only needed on one page type wastes bandwidth and slows the site down. If you want to understand how much overhead plugins add even when their features are not in use, see how building a WordPress site with only core and no plugins dramatically changes performance baselines.


Security: The Non-Negotiable Checklist

Security is not an afterthought in plugin development - it is part of the basic requirements. WordPress plugin vulnerabilities are one of the most common attack vectors for WordPress sites. Every form submission, AJAX request, and data output in your plugin needs security treatment.

Nonces - Verify Request Origin

A nonce (number used once) is a one-time token that verifies a form submission or AJAX request came from your own admin page, not from an external site or a forged request. Always add nonces to forms and verify them on submission.

Capability Checks - Verify User Permissions

Even if a nonce passes, always check that the current user has permission to perform the action. A subscriber with a valid nonce should not be able to delete posts. Use current_user_can() to check capabilities before taking any privileged action.

Sanitization and Escaping

The rule is simple: sanitize on the way in, escape on the way out. When data comes from user input ($_POST, $_GET, database), sanitize it. When data goes into HTML output, escape it. These are different operations with different functions.

Scenario

Function

Text input (no HTML)

sanitize_text_field()

HTML content

wp_kses_post()

Email address

sanitize_email()

Integer value

intval() or absint()

URL input

esc_url_raw()

Output in HTML

esc_html()

Output in attribute

esc_attr()

Output URL in href

esc_url()

Output in JavaScript

esc_js()


The Uninstall.php File

When a user deletes a plugin (not just deactivates it), WordPress looks for an uninstall.php file in the plugin folder. This file should remove all traces the plugin left behind: database tables, options, user meta, and transients. Do NOT delete user-generated content here - only remove plugin settings and infrastructure.

The WP_UNINSTALL_PLUGIN constant check is mandatory. Without it, this file could be called directly by anyone who discovers its URL, deleting all your plugin’s data maliciously. WordPress sets this constant when it runs the uninstall process.


Plugin Internationalization (i18n)

Making your plugin translatable from the start is good practice even if you never plan to translate it yourself. Wrap all user-facing strings in translation functions. The text domain in these functions must match the one in your plugin header.

Use __( 'string', 'text-domain' ) when you need the translated string as a PHP value. Use _e( 'string', 'text-domain' ) when you want to echo it directly. Use _n() for plural strings. Load your text domain on the plugins_loaded action hook.


Common Plugin Development Mistakes to Avoid

After reviewing dozens of plugin codebases, the same mistakes appear again and again. Avoiding these from the start saves significant debugging time later.

  • Using direct database queries instead of the Settings API - Use update_option(), get_option(), and delete_option() for plugin settings. Never write raw SQL for settings storage.
  • Forgetting to prefix function and class names - WordPress is a shared environment. Your function named get_notes() will conflict with another plugin’s function of the same name. Always prefix: snm_get_notes().
  • Outputting before headers are sent - Any output before the HTTP headers are sent (including white space before the opening PHP tag) will cause the “headers already sent” error and break redirects.
  • Not checking return values - WordPress functions like get_post() and wp_insert_post() can return false or WP_Error. Always check before using the result.
  • Enqueuing scripts on every page - Only load what you need where you need it.
  • Trusting user input without sanitization - Every $_POST or $_GET value is untrusted. Sanitize everything before using it.

Testing Your Plugin

Test your plugin thoroughly before deploying it. The WordPress community maintains a set of testing tools and standards that professional plugin developers follow.

Manual Testing Checklist

  • Activate the plugin - does it throw any PHP errors or warnings?
  • Deactivate and reactivate - does the plugin handle this cycle cleanly?
  • Test all admin pages with different user roles (admin, editor, subscriber)
  • Submit forms with unexpected input (empty values, special characters, very long strings)
  • Check that plugin output is properly escaped in the browser’s source view
  • Test with other popular plugins active to check for conflicts
  • Verify the plugin works with the default WordPress themes

Automated Testing with PHPUnit

WordPress provides a testing framework built on PHPUnit. Setting up automated tests requires some initial configuration but pays off quickly for complex plugins. The WordPress Plugin Boilerplate and similar scaffolding tools include test setup out of the box.

Write unit tests for your sanitization callbacks, option handling, and any logic that processes user input. Integration tests are valuable for testing database interactions and hook-based behavior.


Structuring Larger Plugins

As your plugin grows beyond a single file, good organization becomes important. A standard structure that scales well looks like this:

Separate admin functionality from public-facing functionality. Use autoloading if you have many classes. Keep templates in a templates/ or views/ directory. The includes/ directory holds shared classes like the database handler, API wrappers, and utility functions.

Using Composer in Plugins

Modern WordPress plugin development increasingly uses Composer for dependency management and autoloading. If your plugin uses third-party PHP libraries, Composer handles installation and updates. Add your vendor/ directory to .gitignore and commit composer.json and composer.lock. If you are curious how AI tools can speed up this scaffolding work, check out how AI WordPress development can automate theme and plugin creation tasks that used to take hours.


Submitting to the WordPress Plugin Repository

Once your plugin is functional and follows WordPress coding standards, you can submit it to the official WordPress Plugin Repository at wordpress.org/plugins. The review process checks for security issues, guideline compliance, and coding standards. Key requirements include:

  • GPL v2 or compatible license
  • No external calls to non-WordPress.org update servers without user consent
  • No obfuscated or minified code without the original source
  • Proper sanitization and escaping throughout
  • A readme.txt file following the standard format
  • Stable tag pointing to a released version in the SVN repository

Read the Plugin Developer Handbook at developer.wordpress.org before submitting. Plugin reviewers are thorough and the initial review can take several weeks.


Next Steps in Your WordPress Plugin Development Journey

This tutorial covered the foundational concepts every plugin developer needs. The natural progression from here is to explore more advanced topics as your plugins grow in complexity. AJAX handling in plugins, custom post types and taxonomies, the REST API, block editor integration with React-based blocks, and multisite compatibility are all topics that build directly on what you learned here. You may also want to explore how AI coding tools handle WordPress plugin development and where they still fall short compared to understanding the fundamentals yourself.

The WordPress Plugin Boilerplate at wppb.me is a well-structured starting point for new projects - it implements the patterns from this tutorial with a professional folder structure and comprehensive inline documentation. For deeper study, the official Plugin Developer Handbook at developer.wordpress.org covers every API in detail with canonical examples.

The best WordPress plugin is the one you build yourself - tailored exactly to your site's needs, lean, secure, and fully under your control.

Build Your First WordPress Plugin Today

You now have everything you need to build a fully functional WordPress plugin from scratch. Start with the Simple Note Manager example from this tutorial - copy the code, activate it on your local site, and start experimenting. Change the database table structure, add a new settings field, modify the shortcode output. The best way to learn plugin development is to break things in a safe environment and understand why they break.

WordPress plugin development opens up a level of customization that no page builder or theme option panel can match. Once you understand hooks, the Settings API, and WordPress security practices, you can build anything - from simple site utilities to complex SaaS integrations. The skills transfer directly to WooCommerce extensions, BuddyPress add-ons, and any other WordPress-based platform.

Varun Dubey

Written by

Varun Dubey

Varun Dubey runs Wbcom Designs, the WordPress studio he founded in India in 2009. He has spent sixteen years building on WordPress and BuddyPress, shipping client work and products such as Reign, BuddyX, Jetonomy and MediaVerse, and has been putting Claude and OpenAI workflows into production since 2023. He writes up what the studio learns along the way.

More about Varun

No comments yet