When building WordPress plugins that go beyond a few lines of code, organization, reusability, and scalability become critical. This is where the WordPress Plugin Boilerplate comes into play. It offers a standardized, object-oriented foundation for building robust, maintainable plugins. Whether you’re a seasoned developer or just starting out, the boilerplate can save you time, enforce best practices, and streamline your plugin development process. It’s also worth knowing, before committing a new project to it, how it compares to the newer PSR-4/Composer-based boilerplates that have emerged since it was first published, since the ecosystem around plugin scaffolding has moved on in some meaningful ways, and picking the wrong starting structure for a project’s actual scale is a much more expensive mistake to fix six months in than it is to get right on day one.
What is the WordPress Plugin Boilerplate?
The WordPress Plugin Boilerplate is a well-structured, object-oriented foundation for building WordPress plugins. It was originally created by Tom McFarlin and later extended by the open-source community.
It comes pre-loaded with organized folders, base classes, and WordPress best practices, including:
- Object-oriented programming (OOP)
- Proper hook registration
- Namespace separation
- Internationalization readiness
- Separation of admin vs. public functionality
Why Use a Boilerplate?
Using a plugin boilerplate helps you:
- Kickstart plugin projects with clean, consistent code
- Follow WordPress coding standards out of the box
- Save time setting up structure, files, and functions
- Avoid reinventing the wheel with every new plugin
- Separate concerns (admin, public, core logic)
- Improve readability and maintenance over time
File and Folder Structure
Here’s a high-level look at what you get with the default WordPress Plugin Boilerplate:
my-plugin-name/
|-- .gitignore
|-- README.md
|-- uninstall.php
|-- my-plugin-name.php
|-- includes/
| |-- class-my-plugin-name.php
| |-- class-my-plugin-name-activator.php
| |-- class-my-plugin-name-deactivator.php
|-- admin/
| |-- css/
| |-- js/
| |-- partials/
| |-- class-my-plugin-name-admin.php
|-- public/
| |-- css/
| |-- js/
| |-- partials/
| |-- class-my-plugin-name-public.php
|-- languages/
Key Components Explained
1. Main Plugin File (my-plugin-name.php)
This file is the entry point. It initializes core classes and hooks into WordPress.
2. Includes Folder
Contains core loader class and classes for plugin activation and deactivation. These files manage setup logic and plugin registration.
3. Admin Folder
Houses all admin-related functionality like menus, settings pages, admin styles/scripts, etc.
4. Public Folder
Contains code that affects front-end users. Here, you register public hooks and enqueue styles/scripts for the front-end.
5. Languages Folder
Used for internationalization. Includes .pot, .po, and .mo files to support multiple languages.
How the Loader Pattern Actually Works
The part of the boilerplate that trips up developers coming from a simpler procedural background is the Loader class, so it’s worth walking through what it’s actually doing rather than treating it as boilerplate to copy blindly. Instead of calling add_action() and add_filter() directly scattered across files, every hook registration gets queued into an array on the Loader object first, then the Loader’s run() method iterates that array and registers every hook with WordPress core in one pass, right before the plugin actually boots. The benefit isn’t performance, WordPress doesn’t care whether hooks register in one pass or many, it’s a single, inspectable source of truth for every hook the plugin registers. Six months into a project with forty hooks scattered across a dozen files, being able to open one Loader class and see the complete list of what’s wired to what is worth the extra indirection, especially when debugging why a specific action isn’t firing.
How to Use the Boilerplate
- Download or Generate the Boilerplate
- GitHub: https://github.com/DevinVinson/WordPress-Plugin-Boilerplate
- Generator Tool: https://wppb.me/
- Set Your Plugin Details
- Rename files and classes to reflect your plugin name
- Use the generator or find/replace manually (wppb.me makes this easy)
- Begin Development
- Add logic to the
adminandpublicclasses - Register hooks in
includes/class-my-plugin-name.php - Customize the structure as needed for your features
- Add logic to the
- Activate and Test
- Install and activate your plugin in the WordPress admin
- Use
WP_DEBUGto catch any issues early on
The Activator and Deactivator Classes, in Practice
These two small classes get skipped over quickly in most tutorials, but they’re where a surprising number of real-world bugs live, specifically around what belongs in activation versus what belongs in a regular init hook. The activator class runs once, on plugin activation, and is the right place for things that genuinely only need to happen once: creating a custom database table, setting default option values, flushing rewrite rules after registering a custom post type. It’s the wrong place for anything that needs to stay current, registering the custom post type itself belongs on the init hook so it fires on every page load, not just at activation, otherwise the post type silently disappears the moment WordPress core or another plugin flushes the rewrite cache without your activator re-running. The deactivator class mirrors this for cleanup, unscheduling any cron events the plugin registered, flushing rewrite rules again so custom post type URLs don’t 404 after deactivation, without deleting user data, that’s what uninstall.php is for, not deactivation, since a site owner deactivating a plugin temporarily (to troubleshoot a conflict, say) shouldn’t lose their data in the process.
Why uninstall.php Deserves Its Own Attention
The boilerplate ships an uninstall.php file at the plugin root specifically because WordPress treats uninstallation differently from deactivation, it only runs when a user clicks “Delete” from the Plugins screen, not on simple deactivation, and it runs as a standalone script rather than through the plugin’s normal bootstrap. This is genuinely the only correct place to remove a plugin’s custom database tables, delete its options from `wp_options`, and clean up any custom user meta or post meta it created. A security check belongs at the top of this file before anything else runs: if ( ! defined( 'WP_UNINSTALL_PLUGIN' ) ) { exit; } confirms the file is being invoked by WordPress core’s uninstall process rather than being accessed directly, since the file sits in a publicly accessible plugin directory and would otherwise be a way to trigger data deletion from outside WordPress entirely.
Security Practices Worth Building In From the Start
The boilerplate’s structure doesn’t enforce security on its own, it just gives you organized places to put the checks. Every admin-side form submission handled inside the `admin` class needs a nonce check (`check_admin_referer()` or `wp_verify_nonce()`) before acting on submitted data, and every piece of user input needs sanitizing on the way in (`sanitize_text_field()`, `absint()`, and their siblings depending on the expected data type) and escaping on the way out (`esc_html()`, `esc_attr()`, `esc_url()`) when it’s echoed back into HTML. None of this is boilerplate-specific, it’s true of any WordPress plugin, but the boilerplate’s clean separation between admin and public classes makes it easier to audit: every place user input enters the system lives in a small, identifiable set of files rather than being scattered across a monolithic plugin file, which matters a lot when you’re doing a security review months after the initial build and need to quickly locate every input handler.
A Worked Example: Adding a Settings Page End to End
To make the abstract structure concrete, walk through what adding a single admin settings page actually touches across the boilerplate’s files. The setting itself gets registered inside the admin class using the Settings API (`register_setting()`, `add_settings_section()`, `add_settings_field()`), typically hooked to `admin_init` via the Loader. The menu entry pointing to that settings page gets registered separately, hooked to `admin_menu`, also through the Loader, calling a method on the same admin class that renders the actual settings form markup, pulling current values with `get_option()` and echoing them into properly escaped form fields. Enqueuing any CSS or JS specific to that settings page (rather than loading it site-wide in the admin) happens on `admin_enqueue_scripts`, conditionally checked against the current screen ID so the assets don’t load on every single admin page unnecessarily. That’s four separate hook registrations, all visible in one place in the Loader class, all implemented as methods on one admin class, rather than scattered `add_action()` calls buried inside a single sprawling file, which is the entire practical payoff of the pattern once you’ve built more than one feature this way.
Common Mistakes When Customizing the Boilerplate
A handful of mistakes show up repeatedly in plugins built on this structure, worth flagging directly. Renaming the main plugin class but missing a reference to the old name somewhere in the loader or the main plugin file is the most common one, a careful find-and-replace across the entire directory (not just the obviously-named files) catches this before it causes a fatal error on activation. Putting business logic directly inside the admin or public classes instead of a separate service or model class is another common drift, it works fine for a small plugin but makes the admin/public classes balloon in size on anything larger, better to keep them thin (handling only what’s specific to admin-context or public-context rendering) and delegate actual logic to dedicated classes in the includes folder. And forgetting to bump the version number in both the plugin header comment and the `define()` constant inside the main plugin file when releasing an update is a subtle one, WordPress uses the header version for the update-checking mechanism, but plugin code that references the constant for cache-busting asset URLs will keep serving stale cached CSS/JS to already-installed sites if only one of the two gets updated.
Sample Hook Registration
Inside class-my-plugin-name.php:
private function define_admin_hooks() {
$plugin_admin = new My_Plugin_Name_Admin($this->get_plugin_name(), $this->get_version());
$this->loader->add_action('admin_enqueue_scripts', $plugin_admin, 'enqueue_styles');
$this->loader->add_action('admin_enqueue_scripts', $plugin_admin, 'enqueue_scripts');
}
What the Classic Boilerplate Doesn’t Give You
Worth being honest about the gaps, since the original boilerplate hasn’t fundamentally changed its architecture since it was first published, and the PHP ecosystem has moved forward around it. There’s no Composer-based autoloading in the default generator output, classes still get required manually via explicit require_once calls in the loader, which works fine at a small scale but becomes tedious as a plugin grows past a couple dozen classes. There’s no namespacing by default either, classes use the traditional WordPress `Class_Name_With_Underscores` convention specifically to avoid collisions with other plugins, which is a reasonable and battle-tested approach, but it’s a different convention from the PSR-4 namespace-based structure most modern PHP frameworks and packages use. And there’s no built-in dependency injection container, service provider pattern, or any of the architectural patterns common in modern PHP frameworks like Laravel or Symfony; the boilerplate deliberately stays close to WordPress’s own procedural-hook-based mental model rather than importing patterns from outside it.
Modern Alternatives Worth Knowing About
Since the original boilerplate’s release, several PSR-4-native alternatives have emerged that address exactly the gaps above, worth evaluating if a new project’s complexity justifies the extra setup. PolyPlugins maintains a PSR-4 WordPress Plugin Boilerplate that’s effectively a fork of the classic structure with Composer-based namespacing and autoloading layered on, a reasonable middle ground if the classic file/folder layout feels familiar but you want proper autoloading rather than manual requires. The code-soup WordPress Plugin Boilerplate goes further, PSR-4 autoloading, Webpack 5 for asset bundling, a dependency injection container, and a service-provider architecture for organizing features into modular, independently registered providers, built with PHP 8.1+ in mind rather than trying to stay compatible with older PHP versions the way the classic boilerplate does. WPStrap’s boilerplate takes a similar Composer-autoloading-plus-namespace approach with its own dependency manager for bundling third-party libraries cleanly. None of these are objectively “better” than the classic McFarlin boilerplate, they trade the classic version’s broad compatibility and gentle learning curve for more modern PHP tooling and a steeper initial setup, worth picking based on team familiarity with Composer-based PHP development and how large the plugin is actually expected to grow.
Fitting the Boilerplate Into a Real Release Workflow
Beyond the code structure itself, the boilerplate’s included `.gitignore` and `README.md` signal an assumption worth making explicit: this is meant to be developed under version control from day one, not bolted on after the fact. A typical workflow builds features on branches, merges into a development branch, and tags releases against the version number in the plugin header, with the `readme.txt` (the WordPress.org-specific readme, distinct from the GitHub-facing `README.md` the boilerplate ships) changelog kept in sync with each tagged release. For plugins distributed through WordPress.org specifically, that `readme.txt` file isn’t optional decoration, it’s what populates the plugin’s directory listing page, and its “Tested up to” and “Stable tag” fields directly affect whether WordPress flags the plugin as compatible with a site’s current core version. Teams that skip maintaining `readme.txt` alongside actual releases end up with a plugin that works fine but shows an outdated compatibility warning to every site owner considering installing it, a self-inflicted trust problem that has nothing to do with the code itself.
Benefits for Teams and Larger Projects
- Shared understanding of code structure across multiple developers
- Easier code reviews and onboarding
- Reusability of components across plugins
- Support for automated testing and CI/CD integration
Setting Up Automated Testing on Top of the Boilerplate
The boilerplate’s clean separation between activator, deactivator, admin, and public classes pays off directly once you start writing tests, each class has a narrow, well-defined responsibility, which is exactly the property that makes unit testing tractable. A typical setup layers WP_Mock or Brain Monkey on top of PHPUnit to stub out WordPress core functions (add_action, get_option, and so on) without needing a full WordPress installation just to run a test suite, keeping the test suite fast enough to run on every commit rather than only before a release. For anything that does need real WordPress behavior, database interactions, real hook firing order, wp-env or a local Local by Flywheel site paired with the WP-CLI scaffold plugin-tests command sets up an integration test environment closer to production. Wiring this in from the start of a project is meaningfully cheaper than retrofitting tests onto a plugin that’s already grown to thousands of lines without any test coverage.
When Not to Use the Boilerplate
The boilerplate is ideal for medium to large-scale plugins, but may be overkill for very small plugins or utilities. In those cases, a minimal custom structure may be quicker. A genuinely small utility, a single shortcode, a small admin notice tweak, a one-off integration between two other plugins, doesn’t need six files and three folders to organize fifteen lines of actual logic; the ceremony of the full boilerplate structure works against readability at that scale rather than for it. A reasonable rule of thumb: if you can describe everything the plugin does in one sentence and it touches fewer than two or three WordPress hooks total, skip the boilerplate and write a single well-commented file instead.
WordPress Plugin Boilerplate
The WordPress Plugin Boilerplate provides a professional starting point for building plugins using modern development techniques. Whether you’re working solo or as part of a team, this boilerplate enforces a structured approach that pays dividends in code quality, scalability, and maintainability. It’s not the only option anymore, the PSR-4 alternatives above are worth serious consideration for a greenfield project built by a team already comfortable with Composer, but for the broadest compatibility, the gentlest learning curve, and the largest body of existing tutorials and Stack Overflow answers to lean on when you get stuck, the classic boilerplate remains a solid, well-tested default.
Explore it. Fork it. Extend it. And build better WordPress plugins with confidence. Whichever version you land on, classic or PSR-4, the underlying discipline it’s teaching, separating concerns, centralizing hook registration, keeping activation/deactivation/uninstall logic distinct from day-to-day runtime logic, is the part worth internalizing regardless of which specific file structure you end up shipping.