Ways to handle pro and free versions of your WordPress plugin

If you create a “pro” or premium version of your free WordPress plugin, you might run into annoying support issues like:

  1. Users don’t keep their free and pro versions up to date at the same time
  2. Pro/premium purchasers get confused needing to install a free version too (they might not have known there was a free version at all!)
  3. If your pro and free versions conflict, your users get a fatal error (either a message, or a a white screen of death if the plugin is installed and activated outside the WordPress admin)

Here’s some ways I’ve used to eliminate these issues in the free and pro versions of my plugins.

Detect if Free Version is Activated

Regardless of whether you’ll be including the free version inside your pro version or not, you’ll want to know if your free version is loaded.  If you want to show a notice that your free version should be deactivated (ie. you’re bundling it inside the pro version), add this inside the pro version:

function ecn_pro_deactivate_free_version_notice() {
   <div class="notice notice-error is-dismissible">
      <p><?php echo sprintf( __( 'You need to deactivate and delete the old Event Calendar Newsletter plugin on the %splugins page%s', 'event-calendar-newsletter' ), '<a href="' . wp_nonce_url( 'plugins.php?action=deactivate&amp;plugin=event-calendar-newsletter%2Fevent-calendar-newsletter.php&amp;plugin_status=all&amp;paged=1&amp;s=', 'deactivate-plugin_event-calendar-newsletter/event-calendar-newsletter.php' ) . '">', '</a>' ); ?></p>

function ecn_pro_add_core() {
   if ( class_exists( 'ECNAdmin' ) ) {
      add_action( 'admin_notices', 'ecn_pro_deactivate_free_version_notice' );
add_action( 'plugins_loaded', 'ecn_pro_add_core' );

Replacing the class_exists() call with whatever would detect that your free version is loaded.

The link in the admin notice automatically deactivates the plugin when clicked, just change to the slug and file of your plugin.  Then activate your pro and free versions and make sure the link works as expected.

If you want to ensure the free version is activated, you can change the check to something like ! class_exists( ‘ECNAdmin’ ) and change the error message to have a link to your free version.

Include your Free version in the Pro plugin

This is if your pro version depends on the free version.  You can include them inside one zip file for ease of installation and maintenance.

If you have not hard-coded any URLs or paths and used WordPress functions like plugins_url() or relative paths to include files, you should be able to:

  1. Copy your free plugin into a sub-folder, say core/
  2. Delete the plugin header from your free version, otherwise you may hit issues trying to activate your pro plugin
  3. Include the free version inside the pro version with something like this in your main plugin file:
function ecn_pro_add_core() {
   if ( class_exists( 'ECNAdmin' ) ) {
   require_once( 'core/event-calendar-newsletter.php' );
add_action( 'plugins_loaded', 'ecn_pro_add_core' );

Replace the check for class_exists( ‘ECNAdmin’ ) with whatever makes sense for your plugin.  You can include the admin notice and deactivation link as we did in “Detect if Free Version is Activated” above.

Grunt Task to Automatically Include Free Version Changes

Copying changes in your “core” or free version would be annoying to do manually.  Instead, you can create a grunt task to watch for changes and do it automatically.

Install grunt and put this in your package.json file (I put mine inside wp-content/plugins/ folder on my development server):

  "name": "event-calendar-newsletter",
  "version": "0.1.0",
  "devDependencies": {
    "grunt": "~0.4.5",
    "grunt-sync": "~0.2",
    "grunt-contrib-watch": "~0.5.3",
    "grunt-contrib-copy": "~1.0.0"

Then install the dependencies by running:

npm install

You may need to run as sudo depending.  Then pop this in your Gruntfile.js modifying event-calendar-newsletter for your plugin and the “12” in the slice() function call to the number of lines your plugin header contains:

module.exports = function(grunt) {

  // Project configuration.
      watch: {
         files: ['event-calendar-newsletter/**'],
         tasks: ['sync', 'copy']
      sync: {
         main: {
            files: [{
               cwd: 'event-calendar-newsletter',
               src: [
               dest: 'event-calendar-newsletter-pro/core'
            pretend: false,
            verbose: true
      copy: {
         main: {
            src: 'event-calendar-newsletter/event-calendar-newsletter.php',
            dest: 'event-calendar-newsletter-pro/core/event-calendar-newsletter.php',
            options: {
               process: function (content, srcpath) {
                  // remove the plugin header file which will mess things up in the pro version
                  return "<?php\n" + content.split("\n").slice(12).join("\n");


   // Default task(s).
   grunt.registerTask('default', ['watch']);


Then just run grunt and make a change to your files.  This will copy any changes to your plugin files, and strip out the plugin header so it doesn’t mess up the free version.


There’s no one way to handle having a pro and free version, but you’ll want to automate any routine tasks and provide a good experience users will thank you for.

You might also be interested in my Making Pro Plugins course to launch and grow your own WordPress plugin business, which goes through more setup and maintenance tasks like this one so you can get started quickly.

Get a free lesson!

To grab a free lesson and tips on getting over common fears of starting a plugin business, enter your email below.


Already in the course? Login here