All articlesWordPress

How to ship a WordPress plugin with free and Pro tiers — the Freemius way

April 24, 20268 min read

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.

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 costs 20% of revenue, which is discussed 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()->is_paying() ) {
        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' );

is_paying() returns true for active paid subscribers. It returns false for free users, expired trials, and users on lifetime deals that have lapsed. For more granular checks:

  • myplugin_fs()->is_plan('pro') — checks for a specific plan slug
  • myplugin_fs()->can_use_premium_code() — true during trials and for paying users

For most plugins, is_paying() is the right gate. Use can_use_premium_code() if you offer trials and want Pro features accessible during the trial period.

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()->is_paying(),
] );

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 20% cut worth it?

Yes, for most solo developers and small plugin businesses. The honest math: building a checkout, licensing API, and update delivery system takes weeks of engineering time. Maintaining it is ongoing. At 20% revenue share, Freemius pays for itself if you would otherwise spend more than 20% of your time on that infrastructure. For most solo developers that calculation is not close.

The argument for rolling your own only makes sense at significant scale — a high-volume plugin with a team that can dedicate engineering resources to the licensing layer. Below that scale, Freemius is the right answer.


Farhan Shafi
Farhan Shafi
Full-Stack Developer