How to integrate WordPress plugins in a theme

Edward Bock
Write better WordPress code
7 min readOct 18, 2021

--

I want to talk about the similarities and differences between plugins and themes and why and how we use both, but most importantly, how I think the integration of plugins in a theme should be implemented.

Why this article?

There are a few problems that I regularly have or had to do with.

  • A plugin cannot meet new requirements and we have to replace it with another one. But refactoring is complex because plugin functions are used in many different theme template files and functions.
  • There is a weird bug or performance issues and I want to check which plugin could be responsible. But the deactivation of a plugin ends in death of the website 😑.
  • We need some functionality and I know that another project has it. But which parts in the theme do I have to reuse?

Problems of this kind occur when boundaries between theme and plugins are not respected. If you can relate to any of it, you should definitely keep on reading.

Similarities & differences

The WordPress Developer Handbook has to say the following about this.

It is common to find cross-over between features found in themes and plugins. However, best practices are:

- a theme controls the presentation of content; whereas

- a plugin is used to control the behavior and features of your WordPress site.

Any theme you create should not add critical functionality. Doing so means that when a user changes their theme, they lose access to that functionality. For example, say you build a theme with a portfolio feature. Users who build their portfolio with your feature will lose it when they change themes.

By moving critical features to plugins, you make it possible for the design of your website to change, while the functionality remains the same.

But since I really like to talk about code let’s compare both from a pure technical point of view.

There are many technical similarities of themes and plugins. We can say a theme is just a special plugin. You can do everything in a theme that you can do in a plugin and more. So it would be possible to implement a project with all its requirements in a theme only and without any plugin.

On the other hand, plugins cannot use the WordPress template files to build html output for the frontend like themes can. To provide any frontend output with a plugin, other paths must be taken. We will look at the different possibilities with code examples later.

The smallest plugin you can create consists only of a single PHP file with a Plugin Header. The minimum requirements for a theme are two files; index.php and styles.css. There is a special functions.php file for themes which can be considered as the plugin part of the theme.

You can only and have to exactly activate one theme but you can activate as many and as few plugins as you like. Beware that even if you can only activate a single theme, code of a parent theme might also be in use.

In summary, a theme is a special plugin with the responsibility of creating the frontend HTML output. Plugins are outsourced parts of the themes functions.php file.

Implementations of plugin features in a theme

Now that we know the problems as well as the best practices and the technical possibilities, we can think about concrete implementations for interactions between a theme and plugins. In my opinion our goals should be:

  1. Plugins should be theme agnostic
  2. Themes should know as little as possible about the activated plugins
  3. Seamless on/off behavior of theme features when (de)activating plugins
  4. Nice theme developer experience

We’ll talk about the example of the reading time plugin that I used in my previous articles, and look at different approaches to implementing frontend output for this feature. The examples are order from simple to complex, starting with a prerequisite for all the following.

0. Reading time rendering

  1. Actions & filters
  2. Shortcode
  3. Gutenberg block
  4. Theme proxy functions
  5. Theme interface segregation

Without further introduction, let’s dive into code!

0. Reading time rendering

As a prerequisite for all the following examples we expect the reading time plugin to provide a function that renders the html output with a template that can be overwritten in the theme.

You can read about this in my article “Provide overwritable templates from your plugin”.

This is how the rendered output of the plugin will look like. You should always give theme developers the full control of the rendering output and I think the best developer experience is by doing it exactly like WordPress template files work.

All the following examples will use this function and handle the placement of the plugins output on the page or in the theme.

1. Actions & filters

The first thing you could check is if the output can be provided by actions or filters in your plugin. When using those it is possible to provide output without any line of code in the theme.

For any post that has a reading time, it will now be inserted before the content. The downside of this approach is that you have limited placement options, right before or after post content, and another plugin might compete for the place where you want to add your information.

2. Shortcode

The Shortcode API provides a special string syntax for the content editor. It works with both the Classic Editor and the Gutenberg Editor. You can provide a custom shortcode from your plugin as follows:

Use the Gutenberg shortcode block or past the string [reading_time] directly into the Classic Editor. It will be replaced by our shortcode function when rendering in the frontend.

You have a little more options to place the reading time in the post content, but it is still restricted to the content area. Again, no code is required in the theme, which is great, but the user experience for content creators is poor as they have to remember your custom shortcode.

3. Gutenberg block

A much nicer way to allow content creators to add the reading time to the content is a custom Gutenberg block. You have several options for adding a custom block. For demonstration purpose, the following code implements a simple server-side rendered block with no configuration.

If you don’t know how to use modern JavaScript for Gutenberg block development, have a look at “Properly add modern JavaScript to Gutenberg”.

This block has the same advantages as the shortcode, but offers a much nicer user experience because it can be searched in the Gutenberg blocks and you don’t have to remember the exact shortcode.

4. Theme proxy functions

If you want to place the reading time somewhere other than the content area of your post, it’s time to talk about theme templates and functions.php. When theme developers implement plugin functions in theme templates, they usually use function_exists or class_exists to check the availability of the desired function or class in the template file before using it. But this bloats the template files and actually we want as little logic in our template files as possible. It gets even worse when this feature is used in multiple template files, resulting in avoidable duplicate code.

The idea of proxy functions is that you define theme functions in the function.php file for the feature you need in the templates. These functions act as a proxy between the plugins and your theme. They are reliably defined so that an existence check is not necessary.

With this approach, you only need to add a single line of code to your theme templates and encapsulate all the security checks and reasonable fallbacks in the proxy function. This also makes it easy to change implementations or supporting multiple plugins for the same feature.

It would also be possible to extract the template logic for the reading time to a separate template part, but since I want to think of functions.php as the plugin part of the theme, I think proxy functions are the better approach.

5. Theme interface segregation

The idea is to rely on interfaces in the template files rather than implementations. Does that sound familiar? I admit it’s a stolen “I” from the well known SOLID principles.

In my mind, each feature is defined by an interface in the theme. We have a theme singleton instance class that provides access to all features. Plugin integrations are hidden by implementing a feature interface.

Some advantages I see:

  • Autoloading
  • Only a single manual require path in functions.php
  • Runtime caching
  • Easy use of model classes

I pretty sure that this approach could be helpful only for very large projects that you have to maintain for many years. And I mean, it doesn’t bring that much new compared to proxy functions, but looks like much more code.

Anyways I really like the autoloading and object oriented approach that made my plugins a lot more maintainable for me. Also I hate manually adding require paths to the function.php file to exclude features to separate files. Honestly, I have never used this approach in a theme before. I’m waiting for an opportunity to make a proof of concept with this one. Till then it’s only an idea and no recommendation.

Do you have any good or bad experience with an approach like this? Please leave a comment.

Final words

I hope you could take some helpful information with you. If you have any objections or additions feel free to comment.

--

--