My team and I have been working on a music-related WordPress project. For the project, I needed a way to display a different sidebar depending on what page the site visitor was viewing. It’s a question that comes up from time to time from block theme authors.
In classic themes, this always felt straightforward: you just dropped your logic for loading a different template part right in the template itself. While there are architectural design issues with putting logic code in templates—which are largely irrelevant for this post—it simply worked. You could call get_sidebar(), get_header(), get_footer(), or get_template_part() and pass any variable to the functions’ parameters to get what you needed.
For block themes, you cannot do that in HTML-based templates. Even in PHP-registered patterns, you don’t have access to most data before they are processed (patterns are registered on init), so contextually loading template parts won’t work.
The answer for block themes has been to use multiple top-level templates. Create a template for this category, another one for that tag, a third for something else entirely. It works, but it also means maintaining a growing pile of nearly identical templates that differ only in which template part they load.
There’s a better way. WordPress lets you intercept block data before it renders and swap out a template part’s slug on the fly—replacing a default template part with a context-specific one. And while my use case is sidebars, the same technique works for any template part: headers, footers, banners, comments sections, whatever your theme needs.
This tutorial will walk you through how to build this system, starting with the core filter and expanding into broader use cases.
Table of Contents
The render_block_data filter hook
The render_block_data filter hook fires just before any block is rendered. It gives you the parsed block data as an array—including the block name and its attributes—and lets you modify that data before WordPress does anything with it.
For the Template Part block (core/template-part), the most useful attribute is slug, which tells WordPress which template part file to load. Change that slug, and WordPress loads a completely different template part.
Here’s the filter signature:
add_filter( 'render_block_data', 'themeslug_render_template_part_data' );
function themeslug_render_template_part_data( array $parsed_block ): array
{
// Your logic here.
return $parsed_block;
}
The key rule: always return $parsed_block, whether you’ve modified it or not. This filter runs for every block on the page, so the first thing you’ll want to do in any implementation is bail early for blocks you don’t care about.
Building the category-based sidebar
Let’s walk through a concrete example. For the music project, it only needed a sidebar on single post views, named sidebar-post.html (located in the theme’s /parts folder), so this is a narrow use case that we can look at.
In my theme’s templates/single-post.html file, the sidebar template is called via this markup:
<!-- wp:template-part {"slug":"sidebar-post"} /-->
Here’s what the normal template part looks like when viewing the site:

As shown above, it displays the latest posts and a subscription button. But for this project, I needed single post sidebars to display different data for two additional scenarios:
sidebar-post-artist-spotlight.html: Artist Spotlight categorysidebar-post-album-reviews.html: Album Reviews category
Here’s the full filter:
add_filter( 'render_block_data', 'themeslug_render_template_part_data' );
function themeslug_render_template_part_data( array $parsed_block ): array
{
// Only target the Template Part block with a `sidebar-post` slug and
// when viewing a single post.
if (
( $parsed_block['blockName'] ?? '' ) !== 'core/template-part'
|| ( $parsed_block['attrs']['slug'] ?? '' ) !== 'sidebar-post'
|| ! is_singular( 'post' )
) {
return $parsed_block;
}
$post = get_queried_object();
if ( ! $post instanceof WP_Post ) {
return $parsed_block;
}
// Get the directory where template parts live.
$parts_dir = get_block_theme_folders()['wp_template_part'];
// Loop through the post's categories and look for a matching template part.
foreach ( get_the_category( $post->ID ) as $category ) {
$slug = "sidebar-post-{$category->slug}";
if ( locate_template( "{$parts_dir}/{$slug}.html" ) ) {
$parsed_block['attrs']['slug'] = $slug;
return $parsed_block;
}
}
// No category-specific part found; return the original unchanged.
return $parsed_block;
}
There are a few things worth calling out in this code.
The early return checks at the top are important. This filter fires for every block on every page, so you want to exit as fast as possible for anything that doesn’t match your criteria:
- The block name is
core/template-part. - The slug name is
sidebar-post. - The user is currently viewing a singular post.
- The current queried object is a
WP_Post.
The call to get_block_theme_folders()['wp_template_part'] returns the directory your theme uses for template parts—by default parts.
Notice that if no category-specific template part is found, the function returns $parsed_block without modifying it. That means WordPress loads the original sidebar-post.html—the one defined in the template itself. You get the fallback behavior for free, without any extra logic.
The loop through get_the_category() also handles priority automatically. If a post belongs to multiple categories, the categories are returned in term order. The first matching template part file wins. If you need a different priority order, you can sort the categories however you like before iterating.
Setting up the template parts
For this system to work, you need the template part files in place. At minimum, you need the default parts/sidebar-post.html.
Then add category-specific versions as needed:
parts/sidebar-post-author-spotlight.htmlparts/sidebar-post-album-reviews.html
Only create the ones you actually need. If sidebar-post-author-spotlight.html doesn’t exist, the loop skips it and keeps checking. This is what makes the system resilient: you don’t have to create a template part for every possible category, only the ones where you want something different.
My block markup for sidebar-post.html looks like the following, but yours should fit your theme:
<!-- wp:group {"style":{"layout":{"selfStretch":"fit","flexSize":null}},"layout":{"type":"flex","orientation":"vertical","justifyContent":"stretch"}} -->
<div class="wp-block-group">
<!-- wp:group {"className":"is-style-section-3","style":{"spacing":{"blockGap":"var:preset|spacing|40"}},"layout":{"type":"flex","orientation":"vertical"}} -->
<div class="wp-block-group is-style-section-3">
<!-- wp:heading {"level":3,"className":"is-style-text-widget-heading"} -->
<h3 class="wp-block-heading is-style-text-widget-heading">Latest Posts</h3>
<!-- /wp:heading -->
<!-- wp:query {"queryId":3,"query":{"perPage":3,"pages":0,"offset":0,"postType":"post","order":"desc","orderBy":"date","author":"","search":"","exclude":[],"sticky":"ignore","inherit":false,"taxQuery":null,"parents":[],"format":[]},"className":"is-style-query-numbered"} -->
<div class="wp-block-query is-style-query-numbered">
<!-- wp:post-template {"style":{"spacing":{"blockGap":"var:preset|spacing|40"}},"layout":{"type":"default"}} -->
<!-- wp:group {"style":{"spacing":{"blockGap":"var:preset|spacing|20"}},"layout":{"type":"constrained"}} -->
<div class="wp-block-group">
<!-- wp:post-title {"level":3,"isLink":true} /-->
<!-- wp:group {"className":"is-style-meta","style":{"spacing":{"blockGap":"var:preset|spacing|10"}},"fontSize":"3-xs","layout":{"type":"flex","flexWrap":"nowrap"}} -->
<div class="wp-block-group is-style-meta has-3-xs-font-size">
<!-- wp:post-date {"format":"human-diff","metadata":{"bindings":{"datetime":{"source":"core/post-data","args":{"field":"date"}}}}} /-->
</div>
<!-- /wp:group -->
</div>
<!-- /wp:group -->
<!-- /wp:post-template -->
</div>
<!-- /wp:query -->
</div>
<!-- /wp:group -->
<!-- wp:group {"style":{"spacing":{"blockGap":"var:preset|spacing|30"}},"layout":{"type":"flex","orientation":"vertical","justifyContent":"stretch"}} -->
<div class="wp-block-group">
<!-- wp:paragraph {"className":"is-style-default","style":{"typography":{"textAlign":"center"}},"fontSize":"3-xs","fontFamily":"mono"} -->
<p class="has-text-align-center is-style-default has-mono-font-family has-3-xs-font-size">Join 12K Subscribers</p>
<!-- /wp:paragraph -->
<!-- wp:buttons -->
<div class="wp-block-buttons">
<!-- wp:button {"width":100} -->
<div class="wp-block-button has-custom-width wp-block-button__width-100"><a class="wp-block-button__link wp-element-button">Subscribe</a></div>
<!-- /wp:button -->
</div>
<!-- /wp:buttons -->
</div>
<!-- /wp:group -->
</div>
<!-- /wp:group -->
sidebar-post-artist-spotlight.html and sidebar-post-album-reviews.html could be entirely different, showcasing whatever content you wanted.
Here’s a screenshot of the Album Reviews sidebar in action:

For this case, the sidebar-post-album-reviews.html part is connected to a music album that’s being reviewed. This is handled via a custom Query block variation and post meta. To learn more about that technique, read Building a book review grid with a Query Loop block variation.
The important piece is that this didn’t require a custom single post template for each category. I only needed to add a template part for the section of the page that needed to change. This reduces code duplication.
The render_block_data filter doesn’t get nearly enough attention in the block theme space. It’s a clean, low-footprint way to make your theme layouts genuinely dynamic without duplicating templates. Once you have the pattern down, you’ll find yourself reaching for it for all sorts of things—not just sidebars.
Give it a try and see what you come up with.
Props to @bph, @areziaal, @welcher, and @juanmaguitar for feedback on this article.
Leave a Reply