SponsoredHosinger Special Offer

WP WordPress Theme Development

Build classic and block WordPress themes from scratch: the template hierarchy, The Loop, the_title() and the_content(), template parts, menus, the Customizer, child themes and theme.json.

Template Parts with get_template_part()

WordPress Theme Development Lesson 15 of 30 ~4 min read

Overview

get_template_part() loads a reusable chunk of template from a file such as template-parts/content.php, so the markup for "one post in a list" is written once and shared by the blog, archives and search results.

The problem it solves

Without template parts, index.php, archive.php and search.php each contain an identical <article> block inside their Loop. Change the design of a post card and you must remember to change it in three places.

Creating a template part

Create template-parts/content.php:

<?php
/**
 * Template part: one post inside a list.
 */
?>
<article id="post-<?php the_ID(); ?>" <?php post_class( 'post-card' ); ?>>

    <?php if ( has_post_thumbnail() ) : ?>
        <a class="post-card__image" href="<?php the_permalink(); ?>" aria-hidden="true" tabindex="-1">
            <?php the_post_thumbnail( 'medium_large' ); ?>
        </a>
    <?php endif; ?>

    <header class="post-card__header">
        <?php the_title( sprintf( '<h2 class="post-card__title"><a href="%s" rel="bookmark">', esc_url( get_permalink() ) ), '</a></h2>' ); ?>
        <?php gdc_starter_posted_on(); ?>
    </header>

    <div class="post-card__excerpt">
        <?php the_excerpt(); ?>
    </div>

</article>

And use it from any Loop:

<?php while ( have_posts() ) : the_post(); ?>
    <?php get_template_part( 'template-parts/content' ); ?>
<?php endwhile; ?>

The part runs inside the Loop, so every template tag in it — the_title(), the_permalink() — describes the current post automatically.

Choosing a variation with the second argument

get_template_part( $slug, $name ) looks for {$slug}-{$name}.php first and falls back to {$slug}.php. That gives you a mini template hierarchy:

<?php
// Posts load template-parts/content-post.php, pages content-page.php,
// and any other type falls back to template-parts/content.php.
get_template_part( 'template-parts/content', get_post_type() );

// Or vary by post format: content-video.php, content-quote.php ...
get_template_part( 'template-parts/content', get_post_format() );

Passing data into a template part

Since WordPress 5.5, a third argument passes an array that arrives in the part as $args. Use it instead of global variables:

<?php
// In archive.php
get_template_part(
    'template-parts/content',
    'card',
    array(
        'show_excerpt' => true,
        'heading'      => 'h3',
    )
);
<?php
// In template-parts/content-card.php
$args = wp_parse_args(
    $args,
    array(
        'show_excerpt' => false,
        'heading'      => 'h2',
    )
);

$tag = in_array( $args['heading'], array( 'h2', 'h3', 'h4' ), true ) ? $args['heading'] : 'h2';
?>
<article <?php post_class( 'card' ); ?>>
    <<?php echo tag_escape( $tag ); ?> class="card__title">
        <a href="<?php the_permalink(); ?>"><?php the_title(); ?></a>
    </<?php echo tag_escape( $tag ); ?>>

    <?php if ( $args['show_excerpt'] ) : ?>
        <?php the_excerpt(); ?>
    <?php endif; ?>
</article>

wp_parse_args() fills in defaults for anything the caller left out. The heading tag is checked against a fixed list before it is used, and tag_escape() strips anything that is not a valid tag name on output — two layers, so a typo or a bad value can never inject markup.

A "nothing found" part

<?php if ( have_posts() ) : ?>
    <?php while ( have_posts() ) : the_post(); ?>
        <?php get_template_part( 'template-parts/content', get_post_type() ); ?>
    <?php endwhile; ?>
<?php else : ?>
    <?php get_template_part( 'template-parts/content', 'none' ); ?>
<?php endif; ?>
Tip: get_template_part() searches the child theme first, then the parent. That makes every part you create an override point: a child theme can replace just content-card.php and nothing else.

Practice

  • Move the post markup from index.php into template-parts/content.php.
  • Create content-page.php and load parts with get_post_type() as the name.
  • Create content-none.php with a search form and use it in the else branch.
Create a free account to save which lessons you've finished. Save my progress