Organise WordPress Theme Templates with get_template_part() and Partials

As WordPress themes grow beyond a handful of templates, the same HTML structures — post cards, testimonial blocks, hero sections, pricing tables — start appearing in multiple template files. Copy-pasting keeps things simple at first but becomes a maintenance problem the moment a design change requires updating six different files simultaneously. WordPress’s get_template_part() function is the built-in solution: it loads a partial template file by name, optionally namespaced with a variant suffix, and WordPress automatically looks in child theme directories before falling back to the parent theme. This makes partials overridable by child themes without touching the parent, which is the correct way to distribute extensible theme components. In WordPress 5.5, get_template_part() also received a $args parameter so you can pass data directly into the partial without using global variables or set_query_var(). This article covers the naming convention, the variant suffix pattern, passing variables, and a practical comparison with the older set_query_var() / get_query_var() approach.

Problem: The same post card HTML is duplicated across archive templates, search results, and widget templates. Updating the design requires changing every copy, and child themes cannot override individual partials.

Solution: Extract the repeated HTML into a partial file under template-parts/ and load it with get_template_part(). Pass context data via the $args parameter (WP 5.5+) or set_query_var() for older versions.

File structure and naming convention:

theme/
├── template-parts/
│   ├── content.php              ← loaded by get_template_part( 'template-parts/content' )
│   ├── content-post.php         ← loaded by get_template_part( 'template-parts/content', 'post' )
│   ├── content-page.php         ← loaded by get_template_part( 'template-parts/content', 'page' )
│   └── content-product.php      ← loaded by get_template_part( 'template-parts/content', 'product' )

Calling the partial and passing variables (WP 5.5+ $args approach):

<?php
// archive.php — pass context data directly
while ( have_posts() ) {
    the_post();
    get_template_part( 'template-parts/content', get_post_type(), [
        'show_excerpt' => true,
        'image_size'   => 'medium_large',
        'class'        => 'col-md-4',
    ] );
}

Inside the partial (template-parts/content-post.php), the $args array is available automatically:

<?php
// template-parts/content-post.php
$show_excerpt = $args['show_excerpt'] ?? false;
$image_size   = $args['image_size']   ?? 'thumbnail';
$class        = $args['class']        ?? '';
?>
<article id="post-<?php the_ID(); ?>" <?php post_class( $class ); ?>>
    <?php if ( has_post_thumbnail() ) : ?>
        <?php the_post_thumbnail( $image_size ); ?>
    <?php endif; ?>
    <h2><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></h2>
    <?php if ( $show_excerpt ) : ?>
        <?php the_excerpt(); ?>
    <?php endif; ?>
</article>

For WordPress versions before 5.5 that don't support the $args parameter, pass data via set_query_var():

<?php
// Before calling get_template_part()
set_query_var( 'show_excerpt', true );
get_template_part( 'template-parts/content', 'post' );

// Inside the partial
$show_excerpt = get_query_var( 'show_excerpt', false );

NOTE: A child theme can override any partial by creating a file at the same path. If your parent theme uses get_template_part( 'template-parts/content', 'post' ), a child theme file at child-theme/template-parts/content-post.php will be used instead of the parent's version automatically — no PHP override needed. This is the primary reason to use get_template_part() over direct include or require calls.