WordPress Block Themes
WordPress block themes is a very broad topic, and this post is not a typical post but rather something that will expand over time. It is also not specifically intended for you, the reader, but for internal reasons related to this site—although it may still be useful to you.
In this post, we will try to understand block themes as such and attempt to overcome some limitations I have encountered with them.
It seems that block themes must be created through the WordPress editor, and they need to have the following files in order to function
- 📁Templates
- 📄index.html
- 📄style.css
- 📄functions.php( This file is optional in block themes.)
In general, Block themes have the following structure:
- 📁parts/
- 📄footer.html
- 📄header.html
- 📁patterns/
- 📄example.php
- 📁styles/
- 📄example.json
- 📁templates/
- 📄404.html
- 📄archive.html
- 📄index.html (required)
- 📄singular.html
- 📄functions.php
- 📄screenshot.png
- 📄style.css (required)
- 📄theme.json
- 📄README.txt
El file style.css
The style.css file must have a header, which is a comment similar to the following:
style.css/** * Theme Name: Fabled Sunset * Theme URI: https://example.com/fabled-sunset * Description: Custom theme description... * Version: 1.0.0 * Author: Your Name * Author URI: https://example.com * Tags: block-patterns, full-site-editing * Text Domain: fabled-sunset * Domain Path: /assets/lang * Tested up to: 6.4 * Requires at least: 6.2 * Requires PHP: 7.4 * License: GNU General Public License v2.0 or later * License URI: https://www.gnu.org/licenses/gpl-2.0.html */
If the theme is intended for sale, the license must be changed.
Where are the patterns and templates that we create for our WordPress Block Theme stored?
That is a question we should ask ourselves when working with WordPress themes that are Block Themes.
When we create files and folders, we might think that they are being opened and saved. However, that is not the case, and it’s fine that it doesn’t work that way.
Instead, they are stored in our database. They are saved as the following types of entries:
wp_template
: Custom page templates.wp_template_part
: Reusable template parts.wp_global_styles
: Global site styles.
Why is it like this and not directly in the files?
If we think about it, this is the most practical way to work with WordPress themes, since these entries can be deleted. And if we like them, we can copy the pattern or template to a file in our developing theme.
Having duplicates is not a problem—you simply delete the ones in the database, and that’s it.
How can I delete the version stored in the database?
We should go to Appearance > Editor.
Inside the Editor, we should select Templates or Patterns. Then, open a pattern or template. At the very top, you should see a key icon (Create Block Themes). Here, you’ll find a variety of options for the theme, one of which is Reset Theme.

Recommendations for creating WordPress themes.
My personal recommendation is that instead of creating just one theme, you create two: the clean theme you plan to develop and an auxiliary theme where you will design the different patterns and templates.
From the auxiliary theme, copy what you find useful into the main theme.
This will be much more organized than doing everything directly in the theme and having to clean up the database.
PHP code and WordPress block themes
Generally, in block themes, we have almost everything covered—we have the WordPress loop (query loop) and pagination (usually handled within the query loop) taken care of.

Therefore, PHP is generally avoided in block themes, but it is not mandatory to exclude it—although it is not the best approach. We could use functions.php
, but there is a better option: @wordpress/create-block.
In addition to the points mentioned earlier, there is a problem: if you try to create this type of block in WordPress using the HTML block, WordPress will comment out any PHP code.
For example, suppose you have a breadcrumb function and attempt to call it like this:
<?php breadcrumb(); ?>
WordPress will comment out this code for security reasons.
Therefore, the best approach would be to structure the block and copy it into a file—for example, patterns/breadcrumb.php. This way, when we go into the editor, we will have a breadcrumb pattern available.
The ideal solution is to create our own custom block using @wordpress/create-block.
Using blocks and functions.php
.
Example of a breadcrumb pattern:
patterns/breadcrumb.php<?php /** * Title: Breadcrumb * Slug: temaPracticasBLocks/breadcrumb * Categories: common */ ?> <!-- wp:columns {"style":{"border":{"width":"2px","color":"#000000","radius":"7px"}}} --> <div class="wp-block-columns has-border-color" style="border-color:#000000;border-width:2px;border-radius:7px"> <!-- wp:column --> <div class="wp-block-column"> <?php mi_breadcrumb();?> </div> <!-- /wp:column --> </div> <!-- /wp:columns -->
And in the case of our function in functions.php
, it would be the following:
function mi_breadcrumb() { global $wp_query; $message = ""; if (!is_front_page()) { if (is_category() || is_single()) { $message .= get_the_category().">"; } if (is_single()) { the_title(); } elseif (is_page()) { $message .= get_the_title().""; } elseif (is_search()) { $message = 'Resultados de búsqueda para: ' . esc_html(get_search_query()); } $home = esc_url(home_url('/')); $html = <<<HTML <div class="breadcrumb"> <a href="{$home}">Inicio</a> » $message <div> HTML; echo $html; } } add_action('wp', 'mi_breadcrumb');
Okay, if we test it- something unexpected will happen, the error message “Block contains unexpected or invalid” might appear.

What happened?
Well, this is not necessarily a bad thing—it simply means that the editor cannot process the content properly.
The best approach would be to make the content static for this behavior by using if (is_admin())
.
function mi_breadcrumb() { if (is_admin()) { // Static content for preview in the editor } else { // Dynamic content (function logic) } }
The @wordpress/create-block tool
This tool is a Node.js package that helps us create blocks for our theme. These blocks are generated as a plugin, which is one aspect of block themes that might not be ideal—when creating a block theme, you generally expect a theme, not a theme plus a block that functions as a plugin.
There is a great tutorial on how to create a block plugin at WordPress Developer Resources.
In that tutorial, it might mention @wordpress/env, which is an alternative way to run WordPress locally using Node.js.
Although this approach isn’t ideal for the reasons you mentioned earlier, let’s proceed with a small tutorial right here.
We’ll create the same breadcrumb as before, but this time using @wordpress/create-block.
Installing Node.js
Para poder utilizar esta herramienta, necesitarás Node.js.
Linux
You can install it using the binary file. In “Installing Node on Linux“, you’ll find a method to install it on Linux.
Windows
Simply download the installer file and run it, or use Chocolatey to install it if you already have it set up on your Windows machine.
choco install nodejs
Mac
Download the installation file and install it, or use Homebrew to install it
brew install node
In all cases, you can use NVM to install Node, as long as you have NVM installed.
The npx tool
This tool comes as a command in the Node.js installation from npm version 5.2.0, allowing us to execute an npm package, whether local or remote.
Key Advantages:
- No need to install the package on your system (neither globally nor locally).
- Temporary execution – It runs the package without leaving permanent files on your PC.
- Efficient workflow – Perfect for testing or running one-time commands.
Going back to @wordpress/create-block, npx
helps us create a block plugin without needing to install the package permanently, leaving no residual files behind.
Where will we use npx?
In our case, we should use the npx
command inside the plugins folder since we are working within a WordPress installation—this makes it easier to understand.
However, keep in mind that running npx @wordpress/create-block@latest
could be done in any folder and then launched with wp-env
to test the plugin.
For example, suppose I have NGINX installed on my PC in the user directory, then the WordPress installation would be in wthemes
. I should position myself in:
C:\Users\myuser\Nginx\html\wthemes\wp-content\plugins
And then run the following command:
npx @wordpress/create-block@latest block_name
@wordpress/create-block@latest
has many options besides allowing block creation as a plugin. However, we won’t cover them all, as the post would be extremely long.
Below is a list of available options when running the --help
command:
npx @wordpress/create-block@latest --help
Usage: wp-create-block [options] [slug] Generates PHP, JS and CSS code for registering a WordPress plugin with blocks. [slug] is optional. When provided, it triggers the quick mode where it is used as the block slug used for its identification, the output location for scaffolded files, and the name of the WordPress plugin.The rest of the configuration is set to all default values unless overridden with some options listed below. Options: -V, --version output the version number -t, --template project template type name; allowed values: "standard", "es5", the name of an external npm package, or the path to a local directory (default: "standard") --variant the variant of the template to use --no-plugin scaffold only block files --target-dir the directory where the files will be scaffolded, defaults to the slug --namespace internal namespace for the block name --title display title for the block and the WordPress plugin --short-description short description for the block and the WordPress plugin --category category name for the block --wp-scripts enable integration with `@wordpress/scripts` package --no-wp-scripts disable integration with `@wordpress/scripts` package --wp-env enable integration with `@wordpress/env` package -h, --help display help for command Examples: $ wp-create-block $ wp-create-block todo-list $ wp-create-block todo-list --template es5 --title "TODO List"
Understanding what has been created
Let’s remember that our example is a breadcrumb, so we will run the command:
npx @wordpress/create-block@latest breadcrumb
But in addition to this, we need to specify that the variant is dynamic using the flag:
--variant=dynamic
This way, we indicate that the block will be generated or modified dynamically, meaning it will be updated at runtime.
npx @wordpress/create-block@latest --variant=dynamic breadcrumb
Once the plugin is created, if we go to the plugins folder, we should see a folder named breadcrumb.
- 📁breadcrumb
- 📁build
- 📁node_modules
- 📁src
- 📄.editorconfig
- 📄gitignore
- 📄breadcrumb.php
- 📄package.json
- 📄package-lock.json
- 📄readme.txt
Now, if we go to our WordPress editor, we should see our breadcrumb element. However, we’re not done yet—we still need to implement the code for this element.
As it stands, it’s just a decorative component with no real functionality.
Before we start coding, we need to understand the folders we have.
build:
- Is the folder where the compiled code is stored—this is the final generated code. As a plugin, this is the version that should be delivered to the end user.
node_modules:
- It contains the necessary libraries for development.
src:
- It is the “source” folder, where our code will be stored. In our case This is where we will write and modify the core functionality of our breadcrumb block.
rá nuestro código.
As you can see, the folder that will interest us the most is src, although we must understand that we are using Node modules.
That is why, when we use npx @wordpress/create-block@latest --variant=dynamic blockname
, upon completion, it provides the following information:
You can run several commands inside: $ npm start Starts the build for development. $ npm run build Builds the code for production. $ npm run format Formats files. $ npm run lint:css Lints CSS files. $ npm run lint:js Lints JavaScript files. $ npm run plugin-zip Creates a zip file for a WordPress plugin. $ npm run packages-update Updates WordPress packages to the latest version. To enter the directory type: $ cd anotherblockplugin You can start development with: $ npm start
It is likely that, once you execute npx @wordpress/create-block@latest
, you will want to run npm start
. To do this, you must navigate to the folder of your block plugin.
You should also keep in mind that you will need to use the npm start
command whenever you make changes to your CSS and JavaScript.
the src folder
The 📁src folder will contain a folder named with the name setted after npx @wordpress/create-block@latest
command.
For example, if we run:
npx @wordpress/create-block@latest –variant=dynamic breadcrumb
We will have a 📁breadcrumb folder inside src.”
What is the content of
?📁
breadcrumb
- 📁breadcrumb
- 📄block.json
- 📄edit.js
- 📄editor.scss
- 📄index.js
- 📄render.php
- 📄style.scss
- 📄view.js
We could review these files one by one, but the most important of all is 📄render.php.
Let’s take a quick look at the others.
block.json
This will define part of how we see the block in the selector, its properties, and its behavior
{ "$schema": "https://schemas.wp.org/trunk/block.json", "apiVersion": 3, "name": "create-block/breadcrumb", "version": "0.1.0", "title": "Breadcrumb", "category": "widgets", "icon": "smiley", "description": "Example block scaffolded with Create Block tool.", "example": {}, "supports": { "html": false }, "textdomain": "breadcrumb", "editorScript": "file:./index.js", "editorStyle": "file:./index.css", "style": "file:./style-index.css", "render": "file:./render.php", "viewScript": "file:./view.js" }
For example, here we could change the icon, description, etc.
You can see which other icons you can use at the following link.
In theory, you could also insert an SVG directly using: "icon": '//what the SVG has'
.
edit.js
It is also part of how we see our block in the editor.
So, if you modify block.json
, you might need to check this file to see if anything needs adjustments.
Generally, you won’t have much to change.
editor.scss
The 📄editor.scss file is part of the styling of how we will see our block in the WordPress editor.
One important thing to keep in mind is that this file is a Sass file, so you will need to process it.
editor.scss/** * The following styles get applied inside the editor only. * * Replace them with your own styles or remove the file completely. */ .wp-block-create-block-breadcrumb { border: 1px dotted #f00; }
Index.js
The 📄index.js file is one of the most important when developing the block plugin, along with 📄render.php.
📄index.js is the main entry point for the JavaScript code of your Gutenberg block inside 📁src.
In theory, when we process everything using:
npm run build
We will compile all styles and JavaScript files.
And I say ‘in theory’ because we will test this later, and I have not tried it myself yet.
index.js/** * Registers a new block provided a unique name and an object defining its behavior. * * @see https://developer.wordpress.org/block-editor/reference-guides/block-api/block-registration/ */ import { registerBlockType } from '@wordpress/blocks'; /** * Lets webpack process CSS, SASS or SCSS files referenced in JavaScript files. * All files containing `style` keyword are bundled together. The code used * gets applied both to the front of your site and to the editor. * * @see https://www.npmjs.com/package/@wordpress/scripts#using-css */ import './style.scss'; /** * Internal dependencies */ import Edit from './edit'; import metadata from './block.json'; /** * Every block starts by registering a new block type definition. * * @see https://developer.wordpress.org/block-editor/reference-guides/block-api/block-registration/ */ registerBlockType( metadata.name, { /** * @see ./edit.js */ edit: Edit, } );
The render.php file
render.php<?php /** * @see https://github.com/WordPress/gutenberg/blob/trunk/docs/reference-guides/block-api/block-metadata.md#render */ ?> <p <?php echo get_block_wrapper_attributes(); ?>> <?php esc_html_e( 'Breadcrumb – hello from a dynamic block!', 'breadcrumb' ); ?> </p>
This file is, practically, the functional core of your block plugin.
Later, we will see how to create our breadcrumb using this file.
For now, we are just describing these important files in our 📁src folder.
style.scss
As indicated by the internal description, the styles defined here will apply both in the editing interface and on the front end of the site.
style.scss/** * The following styles get applied both on the front of your site * and in the editor. * * Replace them with your own styles or remove the file completely. */ .wp-block-create-block-breadcrumb { background-color: #21759b; color: #fff; padding: 2px; }
view.js
view.js/** * Use this file for JavaScript code that you want to run in the front-end * on posts/pages that contain this block. * * When this file is defined as the value of the `viewScript` property * in `block.json` it will be enqueued on the front end of the site. * * Example: * * ```js * { * "viewScript": "file:./view.js" * } * ``` * * If you're not making any changes to this file because your project doesn't need any * JavaScript running in the front-end, then you should delete this file and remove * the `viewScript` property from `block.json`. * * @see https://developer.wordpress.org/block-editor/reference-guides/block-api/block-metadata/#view-script */ /* eslint-disable no-console */ console.log( 'Hello World! (from create-block-breadcrumb block)' ); /* eslint-enable no-console */
Work-in-progress
The text that continues is being created and could change
Knowing this, let’s return to the creation of the breadcrumb block plugin in WordPress block themes
Muy bien, lo primero que deberíamos probar es nuestro block theme tal cual está para entenderlo. Tal vez con un video podamos comprender mejor esto.
This is a research project. This post will gradually increase in the amount of information over time.