Using WordPress Custom Post Type Templates in a Plugin

If you’re a developer and have worked with WordPress, chances are you have used custom post types. When I started customizing WordPress I found myself using custom post types in themes frequently. Applying a custom template is easy since page templates also typically go in the theme directory.

So what happens when you’re writing a plugin and want to use a template for your custom post type? You can stick it in the active theme folder, but that is bad practice since one of the main points of a plugin is to be theme independent.

Filters to the rescue! You can add a filter to the single_template() function to force a template, even if the template is in your plugin folder.

if( !function_exists('get_MY_CUSTOM_POST_TYPE_template') ):
 function get_MY_CUSTOM_POST_TYPE_template($single_template) {
    global $wp_query, $post;
    if ($post->post_type == 'MY_CUSTOM_POST_TYPE'){
        $single_template = plugin_dir_path(__FILE__) . 'MY_CUSTOM_POST_TYPE_template.php';
    }//end if MY_CUSTOM_POST_TYPE
    return $single_template;
}//end get_MY_CUSTOM_POST_TYPE_template function

add_filter( 'single_template', 'get_MY_CUSTOM_POST_TYPE_template' ) ;
/* ========== END REGISTER CUSTOM POST TYPE TEMPLATE ========== */

You will need to replace “MY_CUSTOM_POST_TYPE” with the name of your custom post type. Also make a note of the variable “$single_template”. I have it set to the directory of my plugin folder and then a file name of “MY_CUSTOM_POST_TYPE_template.php” You will need to update this to the directory and filename of your template. If your template was in a subfolder “templates” within your plugin folder, for example, the variable would need to be:

$single_template = plugin_dir_path(__FILE__) . '/templates/MY_CUSTOM_POST_TYPE_template.php';

That’s it – the template in your plugin folder will function the same as a custom template in a theme. Since we are skipping the template hierarchy and assigning a template with a filter, your template file can be named virtually anything and does not need the “Template Name: ” comment at the top.

Most custom post types I create don’t have archives, or if they do, the existing archive templates suits my needs just fine. If your custom post type has an archive and you would like that archive to have a custom template as well, then simply repeat this process with the “page_template” filter.

Bonus: Templates (Almost) Completely Off the Grid

Recently I created a custom post type and template for some landing pages. Since these templates were completely different from the main site, I wanted to get rid of all the scripts and styles in wp_head() which I didn’t intend to use. You could find them all and run wp_deregister_script() on each one but that isn’t a viable solution for the long-term. What if the site owner adds a new plugin or removes an existing one, etc.?

So now we have to remove all scripts and styles, then reapply what we want. To do this we are going to hook into the wp_print_scripts() and wp_print_styles() functions – both of which store an array of which registered and enqueued scripts are going to be printed. Let’s pause for a minute and make sure your plugin scripts and styles are registered. If not, see my previous article and get that out of the way. You do not need to enqueue your scripts or styles since we will insert them into the print functions manually.

To remove all existing scripts and styles we are going to pass an empty array to the wp_print_scripts() and wp_print_styles() functions, like so:

if( !function_exists('clean_wp_head') ):
    function clean_wp_head(){
    if( !function_exists('MY_CUSTOM_POST_TYPE_remove_all_scripts') ):
        function MY_CUSTOM_POST_TYPE_remove_all_scripts() {
            global $wp_scripts;
            $wp_scripts->queue = array();
        }//end MY_CUSTOM_POST_TYPE_remove_all_scripts function

    if( !function_exists('MY_CUSTOM_POST_TYPE_remove_all_styles') AND !is_admin() ):
        function MY_CUSTOM_POST_TYPE_remove_all_styles() {
            global $wp_styles;
            $wp_styles->queue = array();
        }//end MY_CUSTOM_POST_TYPE_remove_all_scripts function

    if ( get_post_type() == 'MY_CUSTOM_POST_TYPE' AND !is_admin() ):
        add_action('wp_print_scripts', 'MY_CUSTOM_POST_TYPE_remove_all_scripts', 100);
        add_action('wp_print_styles', 'MY_CUSTOM_POST_TYPE_remove_all_styles', 100);
    }//end clean_wp_head function

add_action('wp_head', 'clean_wp_head', 1);

A few things to note:

  • You will need to change “MY_CUSTOM_POST_TYPE” to the name of your custom post type
  • I wrapped the remove all scripts/styles functions inside a larger clean_wp_head() function and hooked it to wp_head(). This ensures that our removal functions get added to the wp_print_scripts() and wp_print_styles() functions before they’re executed.
  • When I say this will remove ALL scripts and styles, I mean it. This includes all default WordPress scripts (like jquery) and styles (like the admin bar styles).

Now that we have a clean slate let’s put back what we need, starting with the WordPress admin bar styles. Simply add “admin-bar” (the name of the registered style sheet for the admin bar) to the “$wp_styles->queue” array. It should now look like this:

$wp_styles->queue = array( 'admin-bar' );

Repeat this with each registered (you did register your styles right?) style for your plugin. It might look something like this:

$wp_styles->queue = array( 'admin-bar',’MY_PLUGIN_STYLES’, ‘MY_PLUGIN_OTHER_STYLES’ );

“MY_PLUGIN_STYLES” and “MY_PLUGIN_OTHER_STYLES” will be the name you gave the style when you registered it with WordPress.

Scripts are the same – just change the “$wp_scripts->queue” array to contain the name of your registered scripts.

$wp_styles->queue = array( ’MY_PLUGIN_SCRIPT’, ‘MY_PLUGIN_OTHER_SCRIPT’ );

Hold on, what about my script dependencies, like jQuery? No need to worry, if you passed it as a dependent when you registered your script with WordPress, it will also be printed- and even in the proper order as if it were regularly enqueued.

Conditional scripts/styles are a bit different. You would normally wrap your conditional statement around the script/style enqueue but in this case, you will need to add the script/style to it’s array conditionally.

So if you want an IE 6 only stylesheet, for example, you would do something like this:

if( preg_match('/(?i)msie [6]/',$_SERVER['HTTP_USER_AGENT']) )
$wp_styles->queue[] = ‘MY_PLUGIN_IE6_STYLES’;’

Just like all other scripts/styles, the ones you use conditionally must be registered with WordPress.

Please comment if you have any questions or concerns. If this helped you out, let me know with a share on the network of your choice! Happy coding.

No Comments

Comments are closed.

Recent Blog Posts in Web Development
The Cyclist at Law website redesign was a great opportunity to create a modern and mobile-responsive twist on its existing theme. Ideally we wanted to personalize the “Cyclist at Law” brand by...
As a landscaping company, Blooms Landcare needed a more vibrant and mobile-responsive site to attract its target audience. Before The Blooms Landcare Website Redesign Mobile Responsive Website Design Mobile responsiveness is more...
Recently we launched a redesign for the Todd Durham Law Firm website. We aimed to simplify the site’s overall design, as well as make it much more mobile-friendly. With Google’s recent update,...