Building a Block-Based Header Template in a Classic Theme

WordPress 6.1 will allow classic theme authors to begin using block-based template parts. It is one of the most exciting features of the release and one that has long been on my personal wish-list.

Over the past couple of weeks, I’ve been doing a deep dive into this new feature, wondering how it could fit into real-world projects. My conclusion was:

  • If building a new classic theme from scratch, block template parts should be relatively easy to insert into it.
  • If adding block template parts into a classic theme, there are likely a few hurdles that you’ll need clear, hoops to jump through, and even potential mountains to climb.

The latter is what this tutorial will focus on. There are 1,000s of classic themes out there in the wild, and the most likely use case for this feature will be the developers who are gradually adopting FSE features.

With that use case in mind, I picked a battle-tested classic theme that is still somewhat recent: Twenty Twenty-One. It already had some of the foundational pieces, such as supporting the block-based content editor. However, it didn’t have support for newer FSE features, such as theme.json.

The other goal for this walk-through was to show a more advanced use case rather than only cover the basics. Ultimately, this helped reveal some of the issues theme authors might face as they go on this journey.

In the end, I created hero header for Twenty Twenty-One that provides users a ton of flexibility for customizing its output on the front end:

Homepage of the Twenty Twenty-One WordPress theme.  The header section has a background of mountains in the night sky.
Hero header built with a block template part.

I invite you to come along with me down this path. I did all of the hard work ahead of time in hopes that it will help those of you who want to give this new feature a try.

If you’d rather skip the walk-through and dive right into the code, no worries. I extracted everything I am covering into this tutorial and put it in a child theme named TT1 Block Parts. Child themes are an easy way to test features without directly changing the parent theme, so I encourage going that route too.

Setting the Groundwork

This feature will be available as part of the WordPress 6.1 release. To test it now, you must either install the WordPress 6.1 Beta 2 or the latest version of the Gutenberg plugin. You will also need a classic theme active on your site. Use Twenty Twenty-One to follow along with each step exactly.

Before building a block-based template part in a classic theme, you must enable the feature via the block-template-parts theme-support flag in your theme’s functions.php file.

<?php add_action( 'after_setup_theme', 'tt1_block_parts_setup' ); function tt1_block_parts_setup() { add_theme_support( 'block-template-parts' ); }
Code language: PHP (php)

This code will create a new Appearance > Template Parts page in the WordPress admin:

Template Parts screen in the WordPress admin. The left column shows a navigation menu and the right displays a no-template-parts-found message.
Initial template parts screen when no parts are registered.

At this point, it’s empty, but you and your users will be able to customize any existing block template parts from this screen once you’ve created them. Let’s do that now.

The next step is building a header template part, which will live in the theme’s parts folder (this is where all block template parts go). The easiest way to get started is to create an empty parts/header.html file. We’ll build this out from the site editor in the next steps.

Now, we need to replace Twenty Twenty-One’s header with our custom one. Open the theme’s header.php template and find this line of code:

<?php get_template_part( 'template-parts/header/site-header' ); ?>
Code language: PHP (php)

Replace it with the following:

<?php block_template_part( 'header' ); ?>
Code language: PHP (php)

It won’t be that easy with every theme. Fortunately, Twenty Twenty-One already separated its primary template code into a PHP-based template part, so we merely needed to change one function call with another. For other themes, it may require removing large chunks of code. It just depends on the theme itself.

Clearing Those Hurdles

Most classic themes were simply not designed to handle blocks outside the content area. This is even true of Twenty Twenty-One because the block-based template editor didn’t exist when it was created.

Each theme is unique, so there is no way for me to realistically foretell what sort of adventures you might go on to get block template parts to work. However, what I can tell you is what I changed with Twenty Twenty-One.

The goal here is transparency rather than glossing over those very likely hurdles you’ll need to jump, but do not let them scare you away.

Adding theme.json Support

Based on my tests with Twenty Twenty-One and other themes, opting into theme.json was almost a must-have. I had the most most success by configuring settings.layout to support wide/full alignments (I used existing CSS properties from the theme for this). I also needed settings.spacing.blockGap to have control over the spacing between menu items in the Navigation block.

With that in mind, I strongly recommend starting with a simple theme.json file in your theme, assuming it doesn’t already have one:

{ "version": 2, "settings": { "layout": { "contentSize": "var( --responsive--default-width )", "wideSize": "var( --responsive--alignwide-width )" }, "spacing": { "blockGap": true } } }
Code language: JSON / JSON with Comments (json)

Style Overrides

I also had to make a few CSS overrides (editor and front end) so that everything was a bit less wonky. Remember, we’re pushing blocks into a place they have never been before in the theme, so some adjustments are a given.

The following CSS is all that was needed to make the block template part work. The most time-consuming aspect was tracking this stuff down in a theme that I wasn’t particularly knowledgeable of.

.wp-block-navigation:not(.has-background) .wp-block-navigation__container { background: transparent; } .wp-block-cover__inner-container > * { max-width: none !important; } .wp-block { max-width: none; } .wp-block-cover .wp-block-site-title.has-text-color a { color: inherit; } .is-root-container > :first-child { margin-top: 0; }
Code language: CSS (css)

Building a Block Header Template Part

Before we get to the more advanced, media-based version of a header template part, let’s stick with the basics. I promise to cover some of the fun stuff as we move along. For now, let’s learn to crawl before attempting to walk.

Go back to the Appearance > Template Parts screen in the WordPress admin that you created in the first step. Click on the “header” template part, which will take you to the template editor. You should see an empty canvas on which to place your creation.

I started with a Cover block. It’s a simple starting point but one of the most flexible blocks in WordPress, providing end-users with tons of customization options out of the gate.

WordPress template editor that displays a header part.  A Cover block with a dark gray background spans across the content canvas.
Adding a Cover block to the Header template part.

I kept my block settings simple and selected the following:

  • Overlay Color: The theme’s default gray
  • Overlay Opacity: 100

Inserting Branding and Navigation

I wanted to keep Twenty Twenty-One’s default header elements in place, which meant recreating the theme’s branding and navigation menu while not going overboard with additional elements.

To achieve this, I added a 50/50 Columns block (the widths of these could be adjusted). In the left column, I inserted Stack with Site Title and Site Tagline. On the right, I placed a Navigation block.

WordPress template editor that displays a header part.  A Cover block with a dark gray background spans across the content canvas, and the branding and nav menu sit in columns on top.
Adding branding and a navigation menu.

The individual settings for these blocks are not particularly important. You can customize them to your liking. The most vital ones are likely justifying the Stack block left and Navigation block right. I also bumped up the block spacing to space out the nav menu items.

This is where you can have a lot of fun, and I don’t want to spoil it with a step-by-step guide on what stylistic settings to choose. Experiment. Go wild!

Extra: Locking Blocks

One of my favorite tools as a theme author is the ability to lock blocks in place with more complex templates or patterns. One of the primary use cases for this is to prevent end-users from accidentally removing an important block, such as the Site Title, or moving things around and being unable to reconfigure them. The site header is one easiest areas to mess up.

For the individual Column block wrapping the Site Title and Site Tagline blocks, I selected all three of the currently-available lock settings:

  • Disable movement
  • Prevent removal
  • Apply to all blocks inside
WordPress template editor that displays a header part.  A popup is over the screen for locking an inner Column block and its nested blocks.
Locking the header branding Column block and its content.

I also set a lock on the outer Columns block to prevent users from inadvertently moving or removing it.

By default, end-users can unlock a block by clicking the “lock” toolbar icon. The extra step primarily serves as a warning to only take this action if they feel comfortable doing so. It’s possible to even prevent this from the development end, but that’s outside the scope of this tutorial. To go even more advanced with block locking, take a read the the Curating the Editor Experience documentation and its section on the Locking APIs.

Copying the Header Part to the Theme

Because we built this template part within the editor, all of our customizations are stored in the database. If planning to distribute this theme to others, you should copy all of the blocks from the editor to your parts/header.html file.

The final code for my template part was:

<!-- wp:cover {"overlayColor":"gray","minHeight":450,"minHeightUnit":"px","style":{"spacing":{"padding":{"top":"4rem","right":"4rem","bottom":"4rem","left":"4rem"}},"color":{}}} --> <div class="wp-block-cover" style="padding-top:4rem;padding-right:4rem;padding-bottom:4rem;padding-left:4rem;min-height:450px"><span aria-hidden="true" class="wp-block-cover__background has-gray-background-color has-background-dim-100 has-background-dim"></span><div class="wp-block-cover__inner-container"><!-- wp:columns {"verticalAlignment":"center","lock":{"move":true,"remove":true}} --> <div class="wp-block-columns are-vertically-aligned-center"><!-- wp:column {"verticalAlignment":"center","width":"50%","templateLock":"all","lock":{"move":true,"remove":true}} --> <div class="wp-block-column is-vertically-aligned-center" style="flex-basis:50%"><!-- wp:group {"lock":{"move":false,"remove":false},"style":{"spacing":{"blockGap":"var:preset|spacing|30"}},"layout":{"type":"flex","orientation":"vertical","justifyContent":"left"}} --> <div class="wp-block-group"><!-- wp:site-title {"style":{"typography":{"textTransform":"uppercase","fontSize":"1.5rem"},"elements":{"link":{"color":{"text":"var:preset|color|white"}}}},"textColor":"white"} /--> <!-- wp:site-tagline {"style":{"typography":{"fontSize":"16px"}},"textColor":"white"} /--></div> <!-- /wp:group --></div> <!-- /wp:column --> <!-- wp:column {"verticalAlignment":"center","width":"50%"} --> <div class="wp-block-column is-vertically-aligned-center" style="flex-basis:50%"><!-- wp:navigation {"ref":2586,"textColor":"white","layout":{"type":"flex","justifyContent":"right"},"style":{"typography":{"fontSize":"16px"},"spacing":{"blockGap":"2rem"}}} /--></div> <!-- /wp:column --></div> <!-- /wp:columns --></div> </div> <!-- /wp:cover -->
Code language: HTML, XML (xml)

After saving that file to your theme, you also need to clear your customizations from the database. You can do this by clicking on the Template Parts admin menu item and selecting the (vertical ellipsis) button for the Header part and clicking the “Clear customizations” option.

WordPress template parts management screen.  The Header template part is being cleared of customizations in the on-screen action.
Clearing customizations from the Header template part.

As you can tell from the above screenshot, I have been tinkering with a lot of template part ideas.

Leveling Up With Patterns and Media

A plain ol’ dark gray header background might not be too exciting. As promised, we’ll kick this up a notch by integrating with another block-related featured that classic themes can opt into: patterns. We’ll use this to add a video background (or, image, if you prefer).

Because block template parts are HTML, it means that you cannot do anything dynamic, such as accurately reference media via your theme’s URL path. You need PHP to do this. That’s where patterns come in.

Instead of putting all of the block code into parts/header.html, you now only need one line of code:

<!-- wp:pattern {"slug":"tt1-block-parts/header-video"} /-->
Code language: HTML, XML (xml)

This references a “Header Video” pattern that we will build in these next steps.

WordPress will automatically register any patterns found in a theme’s patterns folder. So, the next step is to create a patterns/header-video.php file with a few lines of info:

<?php /* * Title: Header - Video * Slug: tt1-block-parts/header-video * Viewport Width: 1024 * Categories: header, twentytwentyone * Inserter: yes */ ?> <!-- Replace with block code. -->
Code language: HTML, XML (xml)

When you’ve built out your header, just remove the <!-- Replace with block code. --> and put the block HTML code in its place.

Let’s jump back to the editor. Using the same header from earlier, we can make a few simple changes to the Cover block to liven things up a bit.

  • Click the “Add Media” toolbar button and select a image or video (your preference).
  • Click the duotone toolbar button to put a filter over the media.
  • Remove the overlay color and adjust the opacity settings in the block inspector sidebar to your liking.
WordPress template editor that displays a header part.  A Cover block spans the screen with a video background of mountains in the night sky.  The Duotone filter is opened to change the media's shadows and highlights.
Adding a video and duotone filter to the Cover block.

Now, you need to copy the block code from the editor and paste it into your patterns/header-video.php file. However, there is one important step you must take afterward. You need to change any references to image or video files that look like the following (there may be more than one occurrence):

http://localhost/wp-content/uploads/2022/09/header-bg.mp4
Code language: JavaScript (javascript)

Those need to reference the media file wherever it lives within your theme. Because I put my header video MP4 into my theme’s assets/video folder, I changed each reference to the following:

<?= esc_url( get_theme_file_uri( 'assets/video/header-bg.mp4' ) ) ?>
Code language: PHP (php)

My final patterns/header-video.php code became:

<?php /* * Title: Header - Video * Slug: tt1-block-parts/header-video * Viewport Width: 1024 * Categories: header, twentytwentyone * Inserter: yes */ ?> <!-- wp:cover {"url":"<?= esc_url( get_theme_file_uri( 'assets/video/header-bg.mp4' ) ) ?>","dimRatio":0,"backgroundType":"video","minHeight":450,"minHeightUnit":"px","isDark":false,"style":{"spacing":{"padding":{"top":"4rem","right":"4rem","bottom":"4rem","left":"4rem"}},"color":{"duotone":["#000000","#D1E4DD"]}}} --> <div class="wp-block-cover is-light" style="padding-top:4rem;padding-right:4rem;padding-bottom:4rem;padding-left:4rem;min-height:450px"><span aria-hidden="true" class="wp-block-cover__background has-background-dim-0 has-background-dim"></span><video class="wp-block-cover__video-background intrinsic-ignore" autoplay muted loop playsinline src="<?= esc_url( get_theme_file_uri( 'assets/video/header-bg.mp4' ) ) ?>" data-object-fit="cover"></video><div class="wp-block-cover__inner-container"><!-- wp:columns {"verticalAlignment":"center","lock":{"move":true,"remove":true}} --> <div class="wp-block-columns are-vertically-aligned-center"><!-- wp:column {"verticalAlignment":"center","width":"50%","templateLock":"all","lock":{"move":true,"remove":true}} --> <div class="wp-block-column is-vertically-aligned-center" style="flex-basis:50%"><!-- wp:group {"lock":{"move":false,"remove":false},"style":{"spacing":{"blockGap":"var:preset|spacing|30"}},"layout":{"type":"flex","orientation":"vertical","justifyContent":"left"}} --> <div class="wp-block-group"><!-- wp:site-title {"style":{"typography":{"textTransform":"uppercase","fontSize":"1.5rem"},"elements":{"link":{"color":{"text":"var:preset|color|white"}}}},"textColor":"white"} /--> <!-- wp:site-tagline {"style":{"typography":{"fontSize":"16px"}},"textColor":"white"} /--></div> <!-- /wp:group --></div> <!-- /wp:column --> <!-- wp:column {"verticalAlignment":"center","width":"50%"} --> <div class="wp-block-column is-vertically-aligned-center" style="flex-basis:50%"><!-- wp:navigation {"ref":2586,"textColor":"white","layout":{"type":"flex","justifyContent":"right"},"style":{"typography":{"fontSize":"16px"},"spacing":{"blockGap":"2rem"}}} /--></div> <!-- /wp:column --></div> <!-- /wp:columns --></div></div> <!-- /wp:cover -->
Code language: PHP (php)

As you did in the earlier step, clear any customizations from your header template part in the admin for the file from the theme to take effect.

Cleaning Up the User Experience

There is one final task that I must ask of you as a theme author. It’s not required, but it is one of those nice-to-haves that will make the user experience a bit nicer.

Remember that theme.json file that we created much earlier in this walk-through? Let’s add a templateParts section to it and register our custom header. We can add a title for our header template part that can be translated.

The final theme.json file:

{ "version": 2, "settings": { "layout": { "contentSize": "var( --responsive--default-width )", "wideSize": "var( --responsive--alignwide-width )" }, "spacing": { "blockGap": true } }, "templateParts": [ { "name": "header", "title": "Header", "area": "header" } ] }
Code language: JSON / JSON with Comments (json)

Now everything is nice and polished. Pat yourself on the back for a job well done if you’ve made it this far. Block-based template parts can be a lot of fun, and they’ll put a ton of customization power into the hands of your users.

Resources To Learn More