Exercise: Templates and template parts

In this exercise, you will learn how to create templates and template parts for block-based themes. You will also create a basic configuration file to handle the width of the content.

Level: Beginner

For this full site editing lesson you will need:

  • A WordPress installation with some test content and the Gutenberg plugin.
  • A blank theme where you will add your block template files.

Estimated reading time: 11 minutes

This lesson has been updated for Gutenberg version 10.4 on April 21 2021

If you created a blank theme in the previous lesson, you can continue using it for this exercise. Remember to install and activate it.
If you have not already set up a theme, you can download part one of the lesson files from GitHub.

Templates are your base files. In this example, you will start with index.html and create additional templates.

Template parts can be used to organize and add structure to the theme to avoid having to repeat code. You will start with a header and footer and will combine them with the template files.

Configuring the content width

In Gutenberg version 10.3 there where changes made to block alignments and widths.
To support custom content width as well as wide and full width blocks, themes now need to include an experimental-theme.json file.

This configuration file will be described in detail in the next two lessons, for now we will only add the content width, so do not worry if you are not familiar with this format yet.

In the main theme folder, create a new file called experimental-theme.json. Copy and paste the following code into the file:

{
	"settings": {
		"defaults" : {
			"layout": {
				"contentSize": "840px",
				"wideSize": "1100px"
			}
		}
	}
}

What this does is set the content width to 840px, and the wide width to 1100px. These two values match the defaults widths in the editors, but you can adjust them if you like.

Creating your first templates

Inside the themes block-templates folder, create an index.html file.
Next, create the footer.html and header.html files inside the block-template-parts folder.

Add content to the template parts so that they can be identified when you test the theme and view the website. The site title and tagline blocks can be added to header.html with the following block markup:

<!-- wp:site-title /-->
<!-- wp:site-tagline /-->

In footer.html, add the well known footer credit text:

<!-- wp:paragraph {"align":"center"} -->
<p class="has-text-align-center">Proudly powered by 
<a href="https://wordpress.org/">WordPress</a>.</p>
<!-- /wp:paragraph -->

To center the block, use both the align:center attribute in the block comment, and the has-text-align-center CSS class on the paragraph.
Need a refresher about block attributes?

Combining templates and template parts

A template part is both an HTML file, a custom post type, and a block. Sounds complicated? Think of them as blocks that display the content that you have placed inside of them.

To include a template part inside a template file, you use the block markup for template parts.
Template parts are self-closing because the content is inside the HTML files.

Open index.html and add the two template parts:

<!-- wp:template-part {"slug":"header"} /-->
<!-- wp:template-part {"slug":"footer"} /-->

Just like blocks, templates and template parts are self-containing.
The opening tag and the closing tag must be in the same template.
You would not be able to place an opening tag for a group block in a header template and close it in a footer template.

For these two important landmarks on our website, we need to use the correct HTML element.
Add the tagName attribute for <header> and <footer>:

<!-- wp:template-part {"slug":"header","tagName":"header"} /-->
<!-- wp:template-part {"slug":"footer","tagName":"footer"} /-->

If you want to be able to identify and target the header and footer with CSS, add a custom CSS className:

<!-- wp:template-part {"slug":"header","tagName":"header","className":"site-header"} /-->
<!-- wp:template-part {"slug":"footer","tagName":"footer","className":"site-footer"} /-->

Next, add support for the layout setting that was added to the experimental-theme.json file.

When the default layout is inherited, the blocks inside the template parts will be centered by default and the width will be the one that you defined for the contentSize. In our example, 840px.
This setting also enables the options for wide width, full width and left, center, and right align.
-You read that right. Without the setting, the blocks will be full width and aligned to the left.

"layout":{"inherit":true}
<!-- wp:template-part {"slug":"header","tagName":"header","className":"site-header","layout":{"inherit":true}} /-->

<!-- wp:template-part {"slug":"footer","tagName":"footer","className":"site-footer","layout":{"inherit":true}} /-->

If you preview your website now, it should look something like this:

The front of the website should display the site title, tagline, and footer credit text.

Adding the blog section

You will create the blog using query and query loop blocks.
But first, you need to add a <main> element as a wrapper for the list of blog posts.

In index.html, between the two template parts, add a group block with the tagName attribute with the value main. Update the <div> to <main>:

<!-- wp:group {"tagName":"main","layout":{"inherit":true}} -->
<main class="wp-block-group">
</main>
<!-- /wp:group -->

Wondering why there is no inner container inside this group?
The inner container was removed for full site editing themes in Gutenberg 10.3.

Query block

The query block has many advanced attributes. For a basic blog, you can rely on using the defaults: Displaying posts ordered by date, descending.

Now you will be adding three blocks at once, inside the group block.
The query block works in combination with the query loop.
The query pagination block lets users navigate between pages of posts. Place the pagination block inside the query, but outside the loop:

<!-- wp:query -->
<!-- wp:query-loop -->

<!-- /wp:query-loop -->

<!-- wp:query-pagination -->
<div class="wp-block-query-pagination">
<!-- wp:query-pagination-previous /-->
<!-- wp:query-pagination-next /-->
</div>
<!-- /wp:query-pagination -->

<!-- /wp:query -->

I want the blog to display the featured image, post title, author, and date above the excerpt. You can experiment with other post blocks, as long as you place them inside the loop:

<!-- wp:query-loop -->
<!-- wp:post-featured-image /-->
<!-- wp:post-title {"isLink":true} /-->
<!-- wp:post-author {"showAvatar":false} /-->
<!-- wp:post-date /-->
<!-- wp:post-excerpt /-->
<!-- /wp:query-loop -->

Not sure what new post blocks you can use? Checkout this reference page.

For the post author block, I decided to not display the avatar, by setting showAvatar to false.

The post title needs to be a link, otherwise, visitors can not reach the individual posts from the blog. "isLink":true will add the correct link for each post inside the loop.

Your index.html should now look like this:

<!-- wp:template-part {"slug":"header","tagName":"header","className":"site-header","layout":{"inherit":true}} /-->
<!-- wp:group {"tagName":"main","layout":{"inherit":true}} -->
<main class="wp-block-group">
	<!-- wp:query -->
	<!-- wp:query-loop -->
	<!-- wp:post-featured-image /-->
	<!-- wp:post-title {"isLink":true} /-->
	<!-- wp:post-author {"showAvatar":false} /-->
	<!-- wp:post-date /-->
	<!-- wp:post-hierarchical-terms {"term":"category"} /-->
	<!-- wp:post-excerpt /-->
	<!-- /wp:query-loop -->
	<!-- wp:query-pagination -->
	<div class="wp-block-query-pagination">
		<!-- wp:query-pagination-previous /-->
		<!-- wp:query-pagination-next /-->
	</div>
	<!-- /wp:query-pagination -->
	<!-- /wp:query -->
</main>
<!-- /wp:group -->
<!-- wp:template-part {"slug":"footer","tagName":"footer","className":"site-footer","layout":{"inherit":true}} /-->

If you remembered to add your kittens, the front of your website should look something like this:

The loop is displaying a featured image with a kitten, holding up it's paws, followed by post title, author name, post date and post excerpt.

But, I think this blog could be improved if the categories were visible too.
The block markup for displaying categories is:

<!-- wp:post-hierarchical-terms {"term":"category"} /-->

This block can be used to display any hierarchical term, and category is only one variation.

<!-- wp:post-featured-image /-->
<!-- wp:post-title {"isLink":true} /-->
<!-- wp:post-author {"showAvatar":false} /-->
<!-- wp:post-date /-->
<!-- wp:post-hierarchical-terms {"term":"category"} /-->
<!-- wp:post-excerpt /-->

Creating post and page templates

I want to show slightly different things for pages and single posts, and to do this, each format needs its own block template.

Save a copy of index.html as single.html inside the block-templates folder.

Of course, we only want to show the current post, so the loop needs to be removed.
What is left in single.html is the following code:

<!-- wp:template-part {"slug":"header","tagName":"header","className":"site-header","layout":{"inherit":true}} /-->
<!-- wp:group {"tagName":"main","layout":{"inherit":true}} -->
<main class="wp-block-group">
	<!-- wp:post-featured-image /-->
	<!-- wp:post-title /-->
	<!-- wp:post-author {"showAvatar":false} /-->
	<!-- wp:post-date /-->
	<!-- wp:post-hierarchical-terms {"term":"category"} /-->
	<!-- wp:post-excerpt /-->
</main>
<!-- /wp:group -->
<!-- wp:template-part {"slug":"footer","tagName":"footer","className":"site-footer","layout":{"inherit":true}} /-->

Since this is a single post, the isLink attribute was removed from the post title.

To show the full content, replace <!-- wp:post-excerpt /--> with
<!-- wp:post-content /-->:

<!-- wp:post-featured-image /-->
<!-- wp:post-title /-->
<!-- wp:post-author {"showAvatar":false} /-->
<!-- wp:post-date /-->
<!-- wp:post-hierarchical-terms {"term":"category"} /-->
<!-- wp:post-content /-->

<!-- wp:post-content /--> Is the equivalent of the_content() in a traditional PHP based theme.

To show wide and full width blocks that are added in the post content, add "align":"full" and "layout":{"inherit":true} to the post content block:

<!-- wp:post-content {"align":"full","layout":{"inherit":true}} /-->

The block markup for displaying tags is simpler than for categories. Add the following code below the content: <!-- wp:post-tags /-->.

If you want to add some extra spacing, this can be achieved by adding spacer blocks between the content and the post meta information:

<!-- wp:post-featured-image /-->
<!-- wp:post-title /-->
<!-- wp:post-author {"showAvatar":false} /-->
<!-- wp:post-date /-->
<!-- wp:post-hierarchical-terms {"term":"category"} /-->
<!-- wp:spacer {"height":40} -->
<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>
<!-- /wp:spacer -->
<!-- wp:post-content {"align":"full","layout":{"inherit":true}} /-->
<!-- wp:spacer {"height":40} -->
<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>
<!-- /wp:spacer -->
<!-- wp:post-tags /-->
<!-- wp:spacer {"height":40} -->
<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>
<!-- /wp:spacer -->

Next and previous post navigation

The post navigation link block was added in Gutenberg version 10.0.
By default, the block adds a link to the next post. The link to the previous posts is a block variation. If there is no next or previous post, the block is not visible on the front of the website. The markup for this new block is:

<!-- wp:post-navigation-link {"type":"previous"} /-->
<!-- wp:post-navigation-link /-->

Displaying comments

You can create a comments area for the blog by adding a heading and a post comments block below the content section, inside the <main> element.

The posts comments block displays both the comments and the comments form, if comments are open.

<!-- wp:spacer {"height":100} -->
<div style="height:100px" aria-hidden="true" class="wp-block-spacer"></div>
<!-- /wp:spacer -->
<!-- wp:heading {"className:"comments-title"} -->
<h2 class="comments-title">Comments</h2>
<!-- /wp:heading -->
<!-- wp:post-comments /-->

It is easy to confuse the post comments block with the post comment block.
Make sure that you add the plural version. The post comment block only displays a single comment with a specific ID.

And here is a view of the single post:

The single post on the front of the website displays the post content, post meta information, and the comment section.

Next, create the block template for single pages: Save a copy of single.html as page.html inside the block-templates folder. In page.html, remove the post navigation, date, author, category and tags:

<!-- wp:template-part {"slug":"header","tagName":"header","className":"site-header","layout":{"inherit":true}} /-->
<!-- wp:group {"tagName":"main","layout":{"inherit":true}} -->
<main class="wp-block-group">
	<!-- wp:post-featured-image /-->
	<!-- wp:post-title /-->
	<!-- wp:spacer {"height":40} -->
	<div style="height:40px" aria-hidden="true" class="wp-block-spacer"></div>
	<!-- /wp:spacer -->
	<!-- wp:post-content {"align":"full","layout":{"inherit":true}} /-->
	<!-- wp:spacer {"height":100} -->
	<div style="height:100px" aria-hidden="true" class="wp-block-spacer"></div>
	<!-- /wp:spacer -->
	<!-- wp:heading {"className:"comments-title"} -->
	<h2 class="comments-title">Comments</h2>
	<!-- /wp:heading -->
	<!-- wp:post-comments /-->
</main>
<!-- /wp:group -->
<!-- wp:template-part {"slug":"footer","tagName":"footer","className":"site-footer","layout":{"inherit":true}} /-->

Save the file, and preview your pages if you like.

Creating the archive templates

As a last step of this exercise, you will create the archive and search result pages using the query and loop blocks.

Open the index.html file in your code editor and save a copy of the file as archive.html.
Or, if you prefer, you can create a more specific template for displaying categories by naming the file category.html, or tags, by naming the file tag.html.

-The process for the search and archive pages is the same, so I will only show you one.

For archives, we do not need to make any changes to the query block, because by default, the block inherits the context:

  • If a visitor is viewing a category archive, the block will display content from this category.
  • If the visitor is viewing the blog, the query block with the default settings will display a list of all the posts.

If the archive block templates are not included, it will fall back to index.html and still show the correct content. The reason why I am suggesting that you create separate templates is to allow for further customization, and to display an archive title.

There is an archive title block that is being developed, but it is not complete yet, so for now, we will add the heading manually.

In your archive file, add an H2 heading block above the query:

<!-- wp:heading {"level":2} -->
<h2>Archive:</h2>
<!-- /wp:heading -->

Categories and tags can optionally have a description. To display the description, you can place the term description block:

<!-- wp:term-description /-->

Remember, if you add the block inside the loop, it will be repeated once for every post.

The full code of the archive.html file is now:

<!-- wp:template-part {"slug":"header","tagName":"header","className":"site-header","layout":{"inherit":true}} /-->
<!-- wp:group {"tagName":"main","layout":{"inherit":true}} -->
<main class="wp-block-group">
	<!-- wp:heading {"level":2} -->
	<h2>Archive:</h2>
	<!-- /wp:heading -->
	<!-- wp:term-description /-->
	<!-- wp:query -->
	<!-- wp:query-loop -->
	<!-- wp:post-featured-image /-->
	<!-- wp:post-title {"isLink":true} /-->
	<!-- wp:post-author {"showAvatar":false} /-->
	<!-- wp:post-date /-->
	<!-- wp:post-hierarchical-terms {"term":"category"} /-->
	<!-- wp:post-excerpt /-->
	<!-- /wp:query-loop -->
	<!-- wp:query-pagination -->
	<div class="wp-block-query-pagination">
		<!-- wp:query-pagination-previous /-->
		<!-- wp:query-pagination-next /-->
	</div>
	<!-- /wp:query-pagination -->
	<!-- /wp:query -->
</main>
<!-- /wp:group -->
<!-- wp:template-part {"slug":"footer","tagName":"footer","className":"site-footer","layout":{"inherit":true}} /-->

Download the theme with the templates and template parts from GitHub.