Add WordPress Plugin Template Files

Written by
Published
Updated
Typical Read
5 minutes

Creating a WordPress plugin where you need to create templates that can be overridden by the theme? Learn how to build a template loading system similar to WooCommerce allowing you to do just that!

If you’ve ever worked with WooCommerce theming, you’ve probably had to copy some of their templates files to your own theme in order to override the HTML. It’s a great system that allows front enders the ability to change the output to match designs. In this article, I’ll walk you through how to do just that — easily add WordPress plugin template files.

Let’s get started!

In this tutorial, we’ll create a templates folder within the plugin where we’ll add our… you guessed it, templates. Here we’ll place the templates that can be overridden in themes. These templates will be loaded via a custom plugin template function that will be called in place of WordPress’ get_template_part function.

Step 1: Locating the right template

In order for this to work, WordPress needs to be able to find the correct template file to load — the plugin or theme template. With the code below, WordPress will first look in the theme’s templates folder, if not found then will search in the main theme’s folder and then if still not found will fallback to the plugin’s templates folder.

<?php
/**
 * Locate template.
 *
 * Locate the called template.
 * Search Order:
 * 1. /themes/theme/templates/$template_name
 * 2. /themes/theme/$template_name
 * 3. /plugins/plugin/templates/$template_name.
 *
 * @since 1.0.0
 *
 * @param   string  $template_name          Template to load.
 * @param   string  $string $template_path  Path to templates.
 * @param   string  $default_path           Default path to template files.
 * @return  string                          Path to the template file.
 */
function PLUGIN_locate_template( $template_name, $template_path = '', $default_path = '' ) {
  // Set variable to search in the templates folder of theme.
  if ( ! $template_path ) :
    $template_path = 'templates/';
  endif;
  // Set default plugin templates path.
  if ( ! $default_path ) :
    $default_path = plugin_dir_path( __FILE__ ) . 'templates/'; // Path to the template folder
  endif;
  // Search template file in theme folder.
  $template = locate_template( array(
    $template_path . $template_name,
    $template_name
  ) );
  // Get plugins template file.
  if ( ! $template ) :
    $template = $default_path . $template_name;
  endif;
  return apply_filters( 'PLUGIN_locate_template', $template, $template_name, $template_path, $default_path );
}

Step 2: Loading the template file

Now that WordPress can find the right template, we need a function to load it. This is what will be used in place of get_template_part.

<?php
/**
 * Get template.
 *
 * Search for the template and include the file.
 *
 * @since 1.0.0
 *
 * @see PLUGIN_locate_template()
 *
 * @param string  $template_name          Template to load.
 * @param array   $args                   Args passed for the template file.
 * @param string  $string $template_path  Path to templates.
 * @param string  $default_path           Default path to template files.
 */
function PLUGIN_get_template( $template_name, $args = array(), $tempate_path = '', $default_path = '' ) {
  if ( is_array( $args ) && isset( $args ) ) :
    extract( $args );
  endif;
  $template_file = contests_locate_template( $template_name, $tempate_path, $default_path );
  if ( ! file_exists( $template_file ) ) :
    _doing_it_wrong( __FUNCTION__, sprintf( '<code>%s</code> does not exist.', $template_file ), '1.0.0' );
    return;
  endif;
  include $template_file;
}

Step 3: Override the WordPress template file

Lastly, we’ll override the default WordPress template file with the code below.

<?php
/**
 * Template loader.
 *
 * The template loader will check if WP is loading a template
 * for a specific Post Type and will try to load the template
 * from out 'templates' directory.
 *
 * @since 1.0.0
 *
 * @param string  $template Template file that is being loaded.
 * @return  string          Template file that should be loaded.
 */
function PLUGIN_template_loader( $template ) {
  $find = array();
  $file = '';
  if( is_singular() ):
    $file = 'single-plugin.php';
  elseif( is_tax() ):
    $file = 'archive-plugin.php';
  endif;
  if ( file_exists( PLUGIN_locate_template( $file ) ) ) :
    $template = PLUGIN_locate_template( $file );
  endif;
  return $template;
}
add_filter( 'template_include', 'PLUGIN_template_loader' );

Putting It All Together

<?php
/**
 * Locate template.
 *
 * Locate the called template.
 * Search Order:
 * 1. /themes/theme/templates/$template_name
 * 2. /themes/theme/$template_name
 * 3. /plugins/plugin/templates/$template_name.
 *
 * @since 1.0.0
 *
 * @param   string  $template_name          Template to load.
 * @param   string  $string $template_path  Path to templates.
 * @param   string  $default_path           Default path to template files.
 * @return  string                          Path to the template file.
 */
function PLUGIN_locate_template( $template_name, $template_path = '', $default_path = '' ) {
  // Set variable to search in the templates folder of theme.
  if ( ! $template_path ) :
    $template_path = 'templates/';
  endif;
  // Set default plugin templates path.
  if ( ! $default_path ) :
    $default_path = plugin_dir_path( __FILE__ ) . 'templates/'; // Path to the template folder
  endif;
  // Search template file in theme folder.
  $template = locate_template( array(
    $template_path . $template_name,
    $template_name
  ) );
  // Get plugins template file.
  if ( ! $template ) :
    $template = $default_path . $template_name;
  endif;
  return apply_filters( 'PLUGIN_locate_template', $template, $template_name, $template_path, $default_path );
}
/**
 * Get template.
 *
 * Search for the template and include the file.
 *
 * @since 1.0.0
 *
 * @see PLUGIN_locate_template()
 *
 * @param string  $template_name          Template to load.
 * @param array   $args                   Args passed for the template file.
 * @param string  $string $template_path  Path to templates.
 * @param string  $default_path           Default path to template files.
 */
function PLUGIN_get_template( $template_name, $args = array(), $tempate_path = '', $default_path = '' ) {
  if ( is_array( $args ) && isset( $args ) ) :
    extract( $args );
  endif;
  $template_file = PLUGIN_locate_template( $template_name, $tempate_path, $default_path );
  if ( ! file_exists( $template_file ) ) :
    _doing_it_wrong( __FUNCTION__, sprintf( '<code>%s</code> does not exist.', $template_file ), '1.0.0' );
    return;
  endif;
  include $template_file;
}
/**
 * Template loader.
 *
 * The template loader will check if WP is loading a template
 * for a specific Post Type and will try to load the template
 * from out 'templates' directory.
 *
 * @since 1.0.0
 *
 * @param string  $template Template file that is being loaded.
 * @return  string          Template file that should be loaded.
 */
function PLUGIN_template_loader( $template ) {
  $find = array();
  $file = '';
  if( is_singular() ):
    $file = 'single-plugin.php';
  elseif( is_tax() ):
    $file = 'archive-plugin.php';
  endif;
  if ( file_exists( PLUGIN_locate_template( $file ) ) ) :
    $template = PLUGIN_locate_template( $file );
  endif;
  return $template;
}
add_filter( 'template_include', 'PLUGIN_template_loader' );

6 comments on “Add WordPress Plugin Template Files”.

# Jul 1, 2018

Здравствуйте! Implement the following template tags to add WordPress-generated class attributes to body, post, and comment elements. For post classes, apply only to elements within The Loop .

Clemens Tolboom

# Jun 7, 2018

Thanks for this post as it helps me to get started.

Unfortunately there are some problems with it.

– What @john said (guard for Post Type).
– What is this function call to contests_locate_template?
– The function PLUGIN_get_template is never called!
– What is apply_filters( ‘PLUGIN_locate_template’,…) for? As there are no filters elsewhere defined I don’t get it.

Cheers!

Caroline

# Jun 2, 2020

To answer your questions Clemens, because I know you’re still just dying to know 😉

– contests_locate_template should be PLUGIN_locate_template. It’s just a little typo.
– Replace PLUGIN with your plugin slug or whatever.
– PLUGIN_get_template would be called by your plugin, it doesn’t need to be called in this code!
– Filters could be accessed by third-party and end users.

Rutger Gabriel

# Apr 5, 2018

Thanks for this example, great way to get started and does exactly what I was looking for.

Shoutout to John’s comment that suggests adding a check, this is quite necessary since any other page that is NOT a custom post type will give an error.

Fabien

# Aug 16, 2017

Hi, I was looking for how to do this, great, thank you 🙂 But to understand, can we have an example of usage? Thank you

John

# Aug 13, 2017

Of all the methods and techniques I have tried, and I have tried several, this one worked! To make it specific to only load my modified templates for my plugin, I added a check in the

"function PLUGIN_template_loader( $template )…" Just before testing for the template to load like this:

function PLUGIN_template_loader( $template ) {

$find = array();
$file = ”;
/ Is this concerning the "Basic Plugin" cpt? /

if (get_post_type() !== "my-basic-test-plugin") { // (or whatever plugin/post type you want to create of course)
return $template;
}
if( is_singular() ):
$file = ‘single-basic-plugin.php’;
elseif( is_tax() ):
$file = ‘archive-basic-plugin.php’;
endif;

Works like a charm!

Sincere THANK YOU!!

Join the conversation.

Your email address will not be published. Required fields are marked *

All comments posted on 'Add WordPress Plugin Template Files' are held for moderation and only published when on topic and not rude. Get a gold star if you actually read & follow these rules.

You may write comments in Markdown. This is the best way to post any code, inline like `<div>this</div>` or multiline blocks within triple backtick fences (```) with double new lines before and after.

Want to tell me something privately, like pointing out a typo or stuff like that? Contact Me.