EthicsComputerCourses
Join training +91 90532 10052

Creating a Block Theme

WordPress · Lesson 21 of 30 Advanced 7 min read Updated

By the end of this lesson you will have built your own block theme from empty files. It will have a header, a footer, a blog list, single post and page layouts, and a ready-made hero pattern. You will understand every file, so you can build themes for clients with confidence.

Building a theme is like setting up a new shop. First the signboard (style.css), then the house rules (theme.json), then the room layouts (templates), the shared entrance and exit (header and footer parts), and finally some ready-made display stands (patterns).

What a block theme needs

Only two files are strictly required:

  • style.css with the theme header
  • templates/index.html, the fallback template for every page

Everything else is optional but useful. Here is the complete theme we will build:

Text
bright-block/
  style.css
  theme.json
  templates/
    index.html
    single.html
    page.html
    404.html
  parts/
    header.html
    footer.html
  patterns/
    hero.php

Step 1: style.css

Create wp-content/themes/bright-block/style.css:

CSS
/*
Theme Name: Bright Block
Author: Your Name
Description: A small block theme for a coaching centre.
Version: 1.0.0
Requires at least: 6.6
Requires PHP: 8.0
License: GNU General Public License v2 or later
Text Domain: bright-block
*/

Go to Appearance > Themes. "Bright Block" already appears, though it will show a message about a missing template until Step 3.

Step 2: theme.json

Add the settings from the previous lesson, plus a list of template parts so the Site Editor knows what they are:

JSON
{
  "$schema": "https://schemas.wp.org/trunk/theme.json",
  "version": 3,
  "settings": {
    "appearanceTools": true,
    "color": {
      "palette": [
        { "slug": "primary", "name": "Primary", "color": "#21759b" },
        { "slug": "accent", "name": "Accent", "color": "#f28c28" },
        { "slug": "base", "name": "Base", "color": "#ffffff" },
        { "slug": "contrast", "name": "Contrast", "color": "#1d2327" }
      ]
    },
    "layout": { "contentSize": "720px", "wideSize": "1200px" }
  },
  "styles": {
    "typography": { "fontFamily": "system-ui, sans-serif", "lineHeight": "1.6" },
    "elements": { "link": { "color": { "text": "var(--wp--preset--color--primary)" } } }
  },
  "templateParts": [
    { "name": "header", "title": "Header", "area": "header" },
    { "name": "footer", "title": "Footer", "area": "footer" }
  ]
}

appearanceTools: true switches on a bundle of design controls in the editor at once, such as borders, padding, margins and link colours.

Template parts live in parts/. Create parts/header.html:

HTML
<!-- wp:group {"layout":{"type":"flex","justifyContent":"space-between"},"style":{"spacing":{"padding":{"top":"1rem","bottom":"1rem"}}}} -->
<div class="wp-block-group" style="padding-top:1rem;padding-bottom:1rem">
  <!-- wp:site-title /-->
  <!-- wp:navigation {"overlayMenu":"mobile"} /-->
</div>
<!-- /wp:group -->

And parts/footer.html:

HTML
<!-- wp:group {"backgroundColor":"contrast","textColor":"base","layout":{"type":"constrained"}} -->
<div class="wp-block-group has-base-color has-contrast-background-color has-text-color has-background">
  <!-- wp:paragraph {"align":"center"} -->
  <p class="has-text-align-center">Bright Future Coaching, Rohtak. Classes 9 to 12.</p>
  <!-- /wp:paragraph -->
</div>
<!-- /wp:group -->

Notice the pattern: the HTML comment describes the block and its settings in JSON, and the HTML inside is what the block saves. For blocks like Site Title and Navigation, the comment ends with /-->, because their HTML is created fresh on every page load. These are called dynamic blocks.

Writing block markup by hand is fiddly. A common trick is to build the design in the editor, switch to the Code editor, copy the markup and paste it into your file.

Step 4: The index template with a Query Loop

Create templates/index.html. It shows a list of posts. The Query Loop block fetches posts, and Post Template repeats its inner blocks for each one:

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

<!-- wp:group {"tagName":"main","layout":{"type":"constrained"}} -->
<main class="wp-block-group">
  <!-- wp:query {"query":{"inherit":true}} -->
  <div class="wp-block-query">
    <!-- wp:post-template -->
      <!-- wp:post-title {"isLink":true} /-->
      <!-- wp:post-date /-->
      <!-- wp:post-excerpt /-->
    <!-- /wp:post-template -->

    <!-- wp:query-pagination -->
      <!-- wp:query-pagination-previous /-->
      <!-- wp:query-pagination-next /-->
    <!-- /wp:query-pagination -->

    <!-- wp:query-no-results -->
      <!-- wp:paragraph --><p>No posts yet. Check back soon.</p><!-- /wp:paragraph -->
    <!-- /wp:query-no-results -->
  </div>
  <!-- /wp:query -->
</main>
<!-- /wp:group -->

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

"inherit": true is important. It means "show whatever posts this URL asks for". So on the blog page it lists the newest posts, on a category page it lists that category, and on a search page it lists the results. The same template works everywhere.

Now activate Bright Block and visit your site. You should see the site title and menu, a list of posts with dates and excerpts, and the dark footer.

Step 5: Single and page templates

Create templates/single.html for individual posts:

HTML
<!-- wp:template-part {"slug":"header","tagName":"header"} /-->
<!-- wp:group {"tagName":"main","layout":{"type":"constrained"}} -->
<main class="wp-block-group">
  <!-- wp:post-title {"level":1} /-->
  <!-- wp:post-date /-->
  <!-- wp:post-featured-image /-->
  <!-- wp:post-content {"layout":{"type":"constrained"}} /-->
  <!-- wp:post-terms {"term":"category"} /-->
</main>
<!-- /wp:group -->
<!-- wp:template-part {"slug":"footer","tagName":"footer"} /-->

Copy it to templates/page.html and remove the date and category lines, because pages have no date or categories. Add a simple templates/404.html with a heading "Page not found" and a Search block (<!-- wp:search {"label":"Search"} /-->).

Open a post on your site. It should use the single layout with the date and categories.

Step 6: A block pattern

Files in patterns/ are registered automatically. Each one is a PHP file with a header comment. Create patterns/hero.php:

PHP
<?php
/**
 * Title: Hero with call to action
 * Slug: bright-block/hero
 * Categories: banner, call-to-action
 * Description: A large heading, a line of text and a demo-class button.
 */
?>
<!-- wp:group {"align":"full","backgroundColor":"primary","textColor":"base","layout":{"type":"constrained"}} -->
<div class="wp-block-group alignfull has-base-color has-primary-background-color has-text-color has-background">
  <!-- wp:heading {"level":1} -->
  <h1 class="wp-block-heading"><?php echo esc_html__( 'Small batches. Big results.', 'bright-block' ); ?></h1>
  <!-- /wp:heading -->
  <!-- wp:buttons -->
  <div class="wp-block-buttons"><!-- wp:button {"backgroundColor":"accent"} -->
  <div class="wp-block-button"><a class="wp-block-button__link has-accent-background-color has-background wp-element-button" href="/contact/"><?php echo esc_html__( 'Book a free demo class', 'bright-block' ); ?></a></div>
  <!-- /wp:button --></div>
  <!-- /wp:buttons -->
</div>
<!-- /wp:group -->

Because the file is PHP, you can make the text translatable. Open the block inserter, click Patterns and look under Banners: "Hero with call to action" is there. You can also place it straight into a template with <!-- wp:pattern {"slug":"bright-block/hero"} /-->.

Step 7: Optional functions.php

Block themes do not need functions.php, but you can add one for extra PHP, such as registering a block style or a custom image size. Keep it small; features that should survive a theme change belong in a plugin.

Common mistakes

  • No templates/index.html. WordPress reports the theme as broken.
  • Broken block markup. A missing closing comment such as <!-- /wp:group --> makes the editor show "This block contains unexpected or invalid content". Copy markup from the Code editor to avoid this.
  • Forgetting inherit: true on the main Query Loop, so category and search pages all show the same latest posts.
  • Editing in the Site Editor and expecting the files to change. Site Editor saves go to the database unless you use Create Block Theme to save them to files.

Key points

  • A block theme needs only style.css and templates/index.html; theme.json, parts and patterns make it useful.
  • Templates and parts are HTML files of block markup; dynamic blocks use self-closing /--> comments.
  • The Query Loop with "inherit": true shows whatever posts the current URL asks for.
  • Template parts in parts/ are pulled into templates with the template-part block.
  • PHP files in patterns/ with a header comment become patterns automatically.
  • The Create Block Theme plugin can save Site Editor work back into theme files.

Exercise

Add an archive.html template to Bright Block that shows the archive title (use the query-title block with {"type":"archive"}), then a Query Loop that inherits the query and shows each post's title, featured image and excerpt in a grid of three columns. Check it by opening a category page.

Show solution
HTML
<!-- wp:template-part {"slug":"header","tagName":"header"} /-->
<!-- wp:group {"tagName":"main","layout":{"type":"constrained"}} -->
<main class="wp-block-group">
  <!-- wp:query-title {"type":"archive"} /-->
  <!-- wp:query {"query":{"inherit":true},"align":"wide"} -->
  <div class="wp-block-query alignwide">
    <!-- wp:post-template {"layout":{"type":"grid","columnCount":3}} -->
      <!-- wp:post-featured-image {"isLink":true} /-->
      <!-- wp:post-title {"isLink":true,"level":3} /-->
      <!-- wp:post-excerpt /-->
    <!-- /wp:post-template -->
  </div>
  <!-- /wp:query -->
</main>
<!-- /wp:group -->
<!-- wp:template-part {"slug":"footer","tagName":"footer"} /-->

Category, tag and date pages now use this template instead of index.html, because archive.html is more specific. The next lesson explains why.

Quick Quiz

1. Which two files does a block theme strictly need?

2. What does "inherit": true on a Query Loop do?

3. Where do template parts live in a block theme?

4. How do you register a pattern in a block theme without any code in functions.php?

Learn WordPress with a mentor

Free lessons get you started. Our WordPress & Website Building training adds real projects and code reviews from working developers.

  • Project-based learning
  • Mentor code reviews
  • Free counselling call