How to build a WordPress block theme

P1045522 scaled How to build a WordPress block theme

Block themes work differently from classic WordPress themes because they use blocks for everything, not just the content area, which means you can edit headers, footers, sidebars, and templates using the Site Editor instead of writing PHP templates, and the configuration lives in a file called theme.json instead of scattered across functions.php and style.css.

What Makes a Block Theme

A block theme needs a few essential files to work, starting with style.css which must have the theme header comment that WordPress uses to identify the theme, but beyond that header, style.css can be mostly empty because styling happens through theme.json and block styles, not traditional CSS files.

/*
Theme Name: My Block Theme
Theme URI: https://example.com
Author: Your Name
Author URI: https://example.com
Description: A simple block theme
Version: 1.0.0
Requires at least: 6.0
Tested up to: 6.9
Requires PHP: 7.4
License: GNU General Public License v2 or later
License URI: http://www.gnu.org/licenses/gpl-2.0.html
Text Domain: my-block-theme
*/

You need a templates folder with at least one file called index.html, and that file must exist for WordPress to recognize your theme as a block theme, even if it is just a basic structure, because WordPress looks for that file to know it can use the Site Editor with your theme.

You can create a parts folder for reusable template parts like headers and footers, and templates can reference those parts using block markup, which keeps things modular so you do not repeat the same header code in every template file.

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

The theme.json file is where you define layout settings like content width and wide width, color palettes, typography settings, block-specific styles, and global theme features, and this file replaces a lot of what you used to do with add_theme_support calls in functions.php, which means less code and more consistency.

Setting Up theme.json

Here is a complete theme.json example that includes layout, colors, and typography settings:

{
  "$schema": "https://schemas.wp.org/trunk/theme.json",
  "version": 2,
  "settings": {
    "layout": {
      "contentSize": "840px",
      "wideSize": "1100px"
    },
    "color": {
      "palette": [
        {
          "slug": "primary",
          "color": "#000000",
          "name": "Primary"
        },
        {
          "slug": "secondary",
          "color": "#ffffff",
          "name": "Secondary"
        }
      ]
    },
    "typography": {
      "fontFamilies": [
        {
          "fontFamily": "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif",
          "slug": "system",
          "name": "System Font"
        }
      ],
      "fontSizes": [
        {
          "slug": "small",
          "size": "0.875rem",
          "name": "Small"
        },
        {
          "slug": "medium",
          "size": "1rem",
          "name": "Medium"
        },
        {
          "slug": "large",
          "size": "1.5rem",
          "name": "Large"
        }
      ]
    }
  },
  "styles": {
    "color": {
      "background": "#ffffff",
      "text": "#000000"
    },
    "typography": {
      "fontFamily": "var(--wp--preset--font-family--system)",
      "fontSize": "var(--wp--preset--font-size--medium)"
    }
  }
}

When you create theme.json, you start with layout settings because those affect how blocks behave, especially wide and full-width alignments, so you might set content width to 840px and wide width to 1100px, and if you do not set these values, wide and full-width blocks will not work properly because WordPress does not know how wide your content area should be.

{
  "$schema": "https://schemas.wp.org/trunk/theme.json",
  "version": 2,
  "settings": {
    "layout": {
      "contentSize": "840px",
      "wideSize": "1100px"
    }
  }
}

You define color palettes in theme.json, and those colors show up in the block editor color picker, which means users can select from your theme colors when styling blocks, and you can also set default colors for text and background, which creates consistency across the site without requiring custom CSS.

{
  "settings": {
    "color": {
      "palette": [
        {
          "slug": "primary",
          "color": "#000000",
          "name": "Primary"
        },
        {
          "slug": "secondary",
          "color": "#ffffff",
          "name": "Secondary"
        }
      ],
      "defaultPalette": false
    }
  }
}

Typography settings go in theme.json too, including font families, font sizes, line heights, and you can define these as presets that appear in the editor, or set defaults that apply globally, and WordPress handles the CSS generation for you, so you do not need to write font-face declarations or manage font loading yourself.

{
  "settings": {
    "typography": {
      "fontFamilies": [
        {
          "fontFamily": "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif",
          "slug": "system",
          "name": "System Font"
        }
      ],
      "fontSizes": [
        {
          "slug": "small",
          "size": "0.875rem",
          "name": "Small"
        },
        {
          "slug": "medium",
          "size": "1rem",
          "name": "Medium"
        },
        {
          "slug": "large",
          "size": "1.5rem",
          "name": "Large"
        }
      ]
    }
  }
}

Block-specific styles can be defined in theme.json, like custom spacing for certain blocks or default settings for columns or groups, and this keeps block styling consistent with your theme design without requiring custom CSS for every block variation.

Building Templates

A minimal index.html template looks like this:

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

<!-- wp:group {"layout":{"type":"constrained"}} -->
<div class="wp-block-group">
  <!-- wp:query-title {"type":"archive"} /-->
  <!-- wp:post-template -->
    <!-- wp:post-title /-->
    <!-- wp:post-excerpt /-->
  <!-- /wp:post-template -->
  <!-- wp:query-pagination /-->
</div>
<!-- /wp:group -->

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

Templates in block themes are HTML files that contain block markup, not PHP files, and they do not include html, head, or body tags because WordPress generates those automatically, so your template files start with the content structure, like a header template part, then the main content area, then a footer template part.

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

<!-- wp:group {"layout":{"type":"constrained"}} -->
<div class="wp-block-group">
  <!-- wp:post-content /-->
</div>
<!-- /wp:group -->

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

The template hierarchy still works the same way as classic themes, so WordPress looks for single.html for single posts, archive.html for archive pages, page.html for static pages, and falls back to index.html if a specific template does not exist, which means you can create as many or as few templates as you need.

Template parts are reusable pieces that you reference from templates, like a header.html or footer.html file in the parts folder, and when you use the template-part block in a template, WordPress loads that part file and inserts it, which means you can update the header once and it changes everywhere it is used.

<!-- wp:group {"layout":{"type":"flex","flexWrap":"nowrap"}} -->
<div class="wp-block-group">
  <!-- wp:site-title /-->
  <!-- wp:navigation /-->
</div>
<!-- /wp:group -->

You can use block patterns in templates too, either by creating custom patterns in a patterns folder or using core patterns, and patterns are predefined block combinations that users can insert, which makes it easier to create consistent layouts without building everything from scratch each time.

Using the Create Block Theme Plugin

The Create Block Theme plugin lets you build themes visually using the Site Editor, and it adds a panel in the WordPress Editor where you can save changes directly to your theme files, create blank themes, clone existing themes, create style variations, or export your theme as a zip file, which is useful when you want to build something visually before diving into code.

When you use the plugin to save changes, it writes your template and style modifications back to the theme files, which means you can design in the Site Editor and then export the code, and the plugin also handles things like bundling images used in templates into the theme assets folder and making sure block markup is export-ready.

You can create style variations using the plugin, which are different color and typography combinations based on the same theme structure, and WordPress shows these variations in the theme picker so users can switch between them, which is useful when you want to offer multiple design options without creating separate themes.

The plugin can create child themes too, which inherit templates and styles from a parent theme but let you override specific parts, and when you create a child theme, the plugin preserves your Site Editor customizations, which means you do not lose your design work when you switch to the child theme.

Working in the Site Editor

The Site Editor is where you build and edit templates visually, and you can see how blocks look and behave as you arrange them, which is different from writing PHP templates where you have to imagine how things will render, and you can test different block combinations quickly without switching between code and browser.

When you edit a template in the Site Editor, you are working with the actual template file, not a preview, so changes you make are saved to the template HTML file, and you can see the block markup that WordPress generates, which helps you understand how blocks translate to HTML when you need to edit files directly.

You can create patterns in the Site Editor too, and patterns are saved as PHP files in a patterns folder, and once you create a pattern, it shows up in the block inserter so you can reuse it across templates or let users insert it into posts and pages.

What You Do Not Need

Block themes do not require functions.php, though you can still add one if you need to enqueue scripts, register custom block styles, or add hooks and filters, but the theme will work without it because most theme functionality is handled through theme.json and block templates.

<?php
// Enqueue custom stylesheet
function my_theme_enqueue_styles() {
  wp_enqueue_style(
    'my-theme-style',
    get_stylesheet_uri(),
    array(),
    wp_get_theme()->get('Version')
  );
}
add_action('wp_enqueue_scripts', 'my_theme_enqueue_styles');

// Register custom image sizes
add_image_size('custom-thumbnail', 300, 300, true);
?>

You do not need to write CSS for basic styling because theme.json handles colors, typography, spacing, and layout settings, and WordPress generates the CSS automatically, though you can still add custom CSS through the Additional CSS panel or a custom stylesheet if you need something that theme.json cannot do.

You do not need to create template files for every post type or archive type if you do not need custom layouts, because WordPress will use index.html as a fallback, which means you can start with just index.html and add more templates as you need them, rather than creating a full set of templates upfront.

Common Things That Break

If you try to add html, head, or body tags to template files, things break because WordPress generates those automatically and wraps your template content in a wp-site-blocks div, so your templates should only contain the content structure, not the full HTML document.

Do not do this in your template files:

<!DOCTYPE html>
<html>
<head>
  <title>My Site</title>
</head>
<body>
  <!-- Your content -->
</body>
</html>

Instead, start directly with block markup:

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

Wide and full-width block alignments will not work if you do not define layout settings in theme.json, because WordPress needs to know your content width to calculate how wide those blocks should be, and if those values are missing, blocks will just use default widths instead of the wide or full-width you intended.

Some plugins and themes built for classic themes assume PHP template files and theme support functions, and they might not work properly with block themes, so you need to test compatibility if you are using older plugins, and some features might require custom code in functions.php to bridge the gap.

If you export a theme using Create Block Theme and the zip file is corrupt, it is usually because WP_DEBUG is set to true in wp-config.php, or because PHP files in your theme have closing tags, so you should set WP_DEBUG to false and remove any ?> tags from the end of PHP files before exporting.

Block Theme File Structure

A minimal block theme has this folder structure:

my-block-theme/
├── style.css
├── theme.json
├── functions.php (optional)
├── screenshot.png (optional)
├── templates/
│   └── index.html (required)
├── parts/
│   ├── header.html (optional)
│   └── footer.html (optional)
└── patterns/
    └── example-pattern.php (optional)

The style.css file only needs the theme header comment, templates folder must contain at least index.html, parts folder is optional but useful for reusable sections, and patterns folder is optional for custom block patterns.

Block Themes vs Classic Themes

Block themes work differently from classic themes in several ways. Classic themes use PHP template files like index.php, single.php, and header.php, while block themes use HTML files with block markup in a templates folder. Classic themes define styles in style.css and theme support in functions.php, while block themes use theme.json for most configuration.

Classic themes require PHP knowledge to build templates and modify layouts, while block themes let you build templates visually in the Site Editor using blocks. Classic themes use template hierarchy with PHP files, block themes use the same hierarchy but with HTML files containing block markup.

In classic themes, you add theme support features using add_theme_support() in functions.php, while block themes define these in theme.json. Classic themes style blocks with CSS, block themes can style blocks through theme.json settings which WordPress converts to CSS automatically.

Classic themes can work without the block editor, block themes require WordPress 5.9 or higher and full block editor support. Classic themes use PHP template parts included with get_template_part(), block themes use template-part blocks that reference HTML files in the parts folder.

Block Theme Cheatsheet

Essential block markup for templates:

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

<!-- Post content -->
<!-- wp:post-content /-->

<!-- Query loops -->
<!-- wp:query -->
  <!-- wp:post-template -->
    <!-- wp:post-title /-->
    <!-- wp:post-excerpt /-->
  <!-- /wp:post-template -->
  <!-- wp:query-pagination /-->
<!-- /wp:query -->

<!-- Site elements -->
<!-- wp:site-title /-->
<!-- wp:site-logo /-->
<!-- wp:navigation /-->
<!-- wp:site-tagline /-->

<!-- Layout containers -->
<!-- wp:group {"layout":{"type":"constrained"}} -->
<!-- wp:group {"layout":{"type":"flex"}} -->
<!-- wp:columns -->

Common theme.json settings:

{
  "settings": {
    "layout": {
      "contentSize": "840px",
      "wideSize": "1100px"
    },
    "color": {
      "palette": [/* color objects */],
      "defaultPalette": false
    },
    "typography": {
      "fontFamilies": [/* font objects */],
      "fontSizes": [/* size objects */]
    },
    "spacing": {
      "padding": true,
      "margin": true
    }
  },
  "styles": {
    "color": {
      "background": "#ffffff",
      "text": "#000000"
    }
  }
}

Required files: style.css with theme header, templates/index.html. Optional files: functions.php, parts/header.html, parts/footer.html, patterns/*.php, screenshot.png.

The Workflow That Works

Most people start by creating a blank theme or cloning an existing one, then they set up theme.json with basic layout and color settings, create index.html in the templates folder with a simple structure, activate the theme, and start building templates in the Site Editor where they can see how things look as they work.

Once templates look right in the Site Editor, you can refine theme.json settings, add more templates for specific post types or archives, create template parts for reusable sections, and build patterns for common layouts, and you can do all of this visually or by editing files directly, depending on what feels easier for each piece.

When you are ready to deploy, you export the theme as a zip file, either using Create Block Theme or by manually packaging the files, and then you can install it on another site or share it, and the exported theme includes all your templates, parts, patterns, theme.json settings, and any assets you bundled into the theme folder.

Block themes shift the work from writing PHP templates to configuring theme.json and building with blocks, which means less code to maintain and more visual building, and once you understand how theme.json controls styling and how templates use block markup, the rest is just arranging blocks until things look right.

Resources