Using WordPress get_template_part in Shortcode Loop

I came across a project recently that required the use of a custom post type for ‘News’ articles. No problem. It was also required that the news articles show on various other pages throughout the site within the page content. Again, no problem, a simple shortcode can handle this without issue.

The issue comes with trying to make the site as simple to maintain as possible. So, should the user ever want to change the layout of how the archive page looks, it would be bad practice to require those changes to be made in multiple places (ie. the archive template and the shortcode function). Enter get_template_part. A simple solution to this is to use a template, in this case simply named news-item.php, to house the layout of the archive items, and call that template instead of coding the loop items directly in the archive page.

Doing things this way will allow me to easily pull in that template when needed, thus if any changes are needed there is only one file to edit. Perfect.

The one small nuance I ran in to was using get_template_part withing the loop in a shortcode. Let’s break down how we can accomplish this.

Creating the Shortcode

First and foremost we need to create the shortcode. We’ll add some shortcode_atts which will allow us to specify different variables within the shortcode when complete. The below code will create the shortcode, with a ‘posts’ attribute (defaulted to 3) and a ‘show_archive’ attribute (defaulted to true). I’ll describe these later.

Note: The following code is part of my core functionality plugin for this site. Bill Erickson has a great post on the need to use a core functionality plugin here.

<?php
function newsItems( $atts ) {
extract( shortcode_atts( array (
     'posts' => 3,
     'show_archive' => 'true',
), $atts ) );
}
add_shortcode('teamsters-news', 'newsItems');

Adding the WordPress Loop to the shortcode

The next thing we’ll do is set up a very simple loop within the shortcode. We start with a blank query variable $news_query.

<?php
function newsItems( $atts ) {
extract( shortcode_atts( array (
     'posts' => 3,
     'show_archive' => 'true',
), $atts ) );

$news_query= null;
$args = array(
     'post_type'      => 'news',
     'post_status'    => 'publish',
     'posts_per_page' => $posts,
);

$news_query = new WP_Query( $args );
if ( $news_query->have_posts() ) {
     while ( $news_query->have_posts() ) : $news_query->the_post();

     endwhile;
}
}
add_shortcode('teamsters-news', 'newsItems');

Adding the template to loop in the shortcode

Next we’ll actually add the template within the loop, along with some other arbitrary HTML. We’ll start with an empty $output variable, and add the content to that within the loop. The problem here is that get_template_part executes php files and generally outputs content, whereas shortcode callbacks are expected to return a string, not output content. What we’ll end up with is the content of the get_template_part displaying above the rest of the content on the page.

The solution? Output buffering. You’ll notice the get_template_part is preceded by ob_start() and followed by appending ob_get_clean() to the $output variable. Essentially what this does is create a buffer prior to the get_template_part function, then resets the buffer and outputs the code into a variable. This allows us to append the template to the $output variable as needed so it displays where and how we need it to.

<?php
function newsItems( $atts ) {
extract( shortcode_atts( array (
     'posts' => 3,
     'show_archive' => 'true',
), $atts ) );

$news_query= null;
$args = array(
     'post_type'      => 'news',
     'post_status'    => 'publish',
     'posts_per_page' => $posts,
);

$news_query = new WP_Query( $args );
$output = '';
if ( $news_query->have_posts() ) {
     $output .= '<div class="news-shortcode-posts">';
     while ( $news_query->have_posts() ) : $news_query->the_post();
          ob_start();
          get_template_part( 'inc/news-item' );
          $output .= ob_get_clean();
     endwhile;
     if( $show_archive == 'true' ) {
          $output .= '<div class="full-width align-right">';
          $output .= '<a class="button-small inverse" href="' . get_home_url() . '/news">See All Archives</a>';
          $output .= '</div>';
     }
     $output .= '</div>';
}
return $output;
}
add_shortcode('teamsters-news', 'newsItems');

This relatively simple approach will allow you to use template parts within shortcodes, which in turn will help create more manageable sites for clients and simpler sites for developers to work with.

About Matt Whiteley

Matt has been building bespoke WordPress websites for over 10 years specializing in the Genesis Framework. He integrates Advanced Custom Fields heavily in all builds, especially with the new Gutenberg Block Editor, allowing clients to easily modify all aspects of the their website with ease.

He works with a wide variety of small businesses and agencies across the country providing development services as well as hosting and maintenance. When he isn't coding he enjoys spending time with his wife and two children, golfing and playing poker.

Reader Interactions

Leave a Reply

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