How to ship a WordPress plugin with free and Pro tiers — the Freemius way
Rolling your own plugin licensing in 2026 is a mistake. Not because it cannot be done, but because the scope is consistently underestimated: checkout flow, payment processing, license key generation, activation and deactivation endpoints, automatic update delivery, refund handling, and revenue reporting. That is a full product before you have shipped a single Pro feature. Freemius handles all of it.
This is the product structure I used for Galleryberg. If you need implementation rather than a reference architecture, see my WordPress plugin development service. For the platform decision itself, compare Freemius with WooCommerce licensing.
What Freemius actually handles
Freemius is a monetisation platform built specifically for WordPress plugins and themes. Out of the box it provides:
- Checkout — a hosted, branded checkout page with Stripe processing. No Stripe account setup on your end.
- License management — key generation, activation limits, deactivation, and verification on plugin load.
- Automatic updates — Pro users receive update notifications and can update directly from the WordPress dashboard, just like free plugins from WP.org.
- Revenue analytics — MRR, ARR, churn, active installs, and upgrade conversion rate in one dashboard.
- Refund handling — Freemius processes refund requests and adjusts license status automatically.
- Opt-in usage analytics — an on-activation prompt that collects PHP/WP version data if the user consents.
The alternative is building each of these yourself. For a solo developer, that is months of infrastructure work. Freemius charges a platform revenue share, discussed with the current documented rate at the end of this post.
Folder structure for the free/Pro split
The recommended structure ships one plugin ZIP to WP.org. The pro/ folder is included in the free package but its files are never loaded unless the license check passes.
myplugin/
├── myplugin.php ← main plugin file, bootstraps Freemius
├── freemius/ ← Freemius SDK (committed to the repo)
├── includes/
│ ├── class-core.php ← shared code, loaded for all users
│ └── class-free-feature.php
├── pro/
│ ├── class-pro-feature.php ← Pro-only, never loaded for free users
│ └── class-pro-export.php
└── assets/
├── css/
└── js/
The key constraint: nothing in pro/ should be autoloaded or included at the top level. All Pro includes are gated behind the license check at runtime.
SDK bootstrap in the main plugin file
The SDK init goes at the top of the main plugin file, before any other includes. The pattern uses a global to make the instance accessible across the plugin:
if ( ! function_exists( 'myplugin_fs' ) ) {
function myplugin_fs() {
global $myplugin_fs;
if ( ! isset( $myplugin_fs ) ) {
require_once dirname( __FILE__ ) . '/freemius/start.php';
$myplugin_fs = fs_dynamic_init( [
'id' => 'YOUR_PLUGIN_ID',
'slug' => 'myplugin',
'type' => 'plugin',
'public_key' => 'pk_YOUR_PUBLIC_KEY',
'is_premium' => false,
'has_addons' => false,
'has_paid_plans' => true,
'menu' => [
'slug' => 'myplugin',
'first-path' => 'plugins.php',
],
] );
}
return $myplugin_fs;
}
myplugin_fs();
do_action( 'myplugin_fs_loaded' );
}
The is_premium flag stays false in the WP.org build. Freemius toggles it automatically for Pro users via their SDK update mechanism — you never change that value manually.
The PHP gating pattern
Once the SDK is bootstrapped, gating Pro features is a single check:
function myplugin_load_pro() {
if ( ! myplugin_fs()->can_use_premium_code__premium_only() ) {
return;
}
require_once plugin_dir_path( __FILE__ ) . 'pro/class-pro-feature.php';
require_once plugin_dir_path( __FILE__ ) . 'pro/class-pro-export.php';
// Instantiate or hook Pro classes here
new MyPlugin_Pro_Feature();
}
add_action( 'plugins_loaded', 'myplugin_load_pro' );
For a single paid plan, Freemius currently recommends can_use_premium_code() or its __premium_only() variant. The suffixed form lets the deployment preprocessor remove the guarded premium code from the generated free package, which matters for WordPress.org compliance. For more granular checks:
myplugin_fs()->is_plan__premium_only('pro')— checks a specific paid plan and strips the guarded code from the free buildmyplugin_fs()->is_plan_or_trial__premium_only('pro')— includes trials for that planmyplugin_fs()->can_use_premium_code()— checks runtime eligibility when code can remain in both packages
Do not treat a plain runtime boolean as a substitute for packaging. If premium PHP must not ship in the WordPress.org build, wrap it in a supported __premium_only() condition so Freemius can remove it during deployment.
On the JavaScript side, pass the license status to the block editor via wp_localize_script:
wp_localize_script( 'myplugin-editor', 'mypluginData', [
'isPro' => myplugin_fs()->can_use_premium_code(),
] );
Then gate UI elements in the block editor:
const isPro = window.mypluginData?.isPro ?? false;
{!isPro && (
<Notice status="warning">
This feature requires the Pro version.{' '}
<a href={upgradeUrl}>Upgrade</a>
</Notice>
)}
WP.org listing strategy
The WP.org listing is the primary acquisition channel for the free tier. A few things that move the needle:
Screenshots over description. The five plugin screenshots are the first thing most potential users see. Use them to show actual use cases — real block output, real editor UI — not settings panels.
Tested up to. Keep this within one release of the current WordPress version. Plugins that show "Tested up to: 6.2" when WordPress is at 6.6 lose installs immediately. Freemius makes it easy to track the WordPress versions your active users run, which helps prioritise compatibility testing.
Upgrade prompt messaging. Freemius injects an upgrade page automatically, but the copy is yours. Specific claims convert better than vague ones. "Custom aspect ratios, AVIF output, and watermarking" outperforms "unlock powerful Pro features" because it tells the user exactly what they are paying for.
Is the current revenue share worth it?
As of this update, Freemius documents a 4.7% platform revenue share per successful transaction, separate from payment-processing fees, with progressively lower rates after $50,000 in monthly gross sales. That is materially different from the older 20% model and changes the build-versus-buy calculation.
The argument for self-hosting still depends on more than the percentage: control over checkout and customer data, existing commerce infrastructure, tax handling, licensing support, update delivery, and the engineering time required to operate all of it. For many small teams, the managed workflow remains the stronger trade.
For block-plugin architecture before monetisation, see the production Gutenberg architecture guide.
Official references
- Freemius WordPress SDK integration
- Freemius software licensing methods
- Freemius pricing documentation
Related articles