<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Silvia Malavasi</title>
    <description>The latest articles on DEV Community by Silvia Malavasi (@silviamalavasi).</description>
    <link>https://dev.to/silviamalavasi</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1290730%2F9e8311f4-6f94-461b-92bd-9e7770631159.png</url>
      <title>DEV Community: Silvia Malavasi</title>
      <link>https://dev.to/silviamalavasi</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/silviamalavasi"/>
    <language>en</language>
    <item>
      <title>Let’s Get Hands-On with WordPress FSE Theme and Custom Blocks — Part 2</title>
      <dc:creator>Silvia Malavasi</dc:creator>
      <pubDate>Fri, 28 Jun 2024 09:45:24 +0000</pubDate>
      <link>https://dev.to/silviamalavasi/lets-get-hands-on-with-wordpress-fse-theme-and-custom-blocks-part-2-13ea</link>
      <guid>https://dev.to/silviamalavasi/lets-get-hands-on-with-wordpress-fse-theme-and-custom-blocks-part-2-13ea</guid>
      <description>&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8ht13qa7yxpvdoizfkrc.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8ht13qa7yxpvdoizfkrc.jpg" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  A Complete Ecosystem
&lt;/h1&gt;

&lt;p&gt;While we can create and compile our own blocks for integration into any FSE theme individually, the optimal efficiency is achieved by constructing an ecosystem that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Automates all build processes&lt;/li&gt;
&lt;li&gt;Establishes a clear folder structure&lt;/li&gt;
&lt;li&gt;Allows for scalable expansion&lt;/li&gt;
&lt;li&gt;Ensures stylistic consistency and facilitates the reuse of common elements across all blocks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So, let’s focus on the theme and develop a build system for our blocks, as well as for our SASS and JavaScript files. It’s important to note that all block-related computations occur only when the block is present on the page we’re viewing. Additionally, any globally used JavaScript functions should have their own file within the theme, following the same approach as traditional themes.&lt;/p&gt;

&lt;h1&gt;
  
  
  The Structure
&lt;/h1&gt;

&lt;p&gt;In contrast to a basic FSE theme structure, we introduce a few additional folders:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;src&lt;/code&gt;: this is where we place our JavaScript and styles that need to be compiled&lt;br&gt;
&lt;code&gt;blocks/custom-blocks&lt;/code&gt;: each block is housed in its own folder within this directory.&lt;br&gt;
&lt;code&gt;dist&lt;/code&gt;: this directory is designated for the compiled theme files&lt;/p&gt;

&lt;p&gt;And the two files that pertain to the build process:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;package.json&lt;/code&gt;&lt;br&gt;
&lt;code&gt;webpack.config.js&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Therefore, before building, our structure resembles the following. If you manage the header and footer exclusively through the editor, you can remove the &lt;code&gt;parts&lt;/code&gt; folder.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

my-theme/
│
├── blocks/
│   └── custom-blocks/
│       └── first-block/
│           ├── block.json
│           ├── edit.js
│           ├── edit.scss
│           ├── index.js
│           ├── save.js
│           ├── save.scss
│       └── second-block/
│           ├── block.json
│           ├── edit.js
│           ├── edit.scss
│           ├── index.js
│           ├── save.js
│           ├── save.scss
│       └── ...
│
├── dist/
│
├── parts/
│   ├── footer.html
│   ├── header.html
│
├── src/
│   ├── css/
│   │   ├── style.scss
│   │   ├── dashboard-style.scss
│   ├── js/
│       ├── site.js
│
├── templates/
│   ├── index.html
│
├── functions.php
├── package.json
├── theme.json
└── webpack.config.js


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Let’s briefly discuss the &lt;code&gt;dashboard-style.scss&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;In WordPress, styles are enqueued in various ways. Block styles, as previously mentioned, are handled directly within each block. Frontend styles are enqueued in &lt;code&gt;functions.php&lt;/code&gt; using &lt;code&gt;wp_enqueue_style&lt;/code&gt; within the &lt;code&gt;wp_enqueue_scripts&lt;/code&gt; action.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

function zenfse_enqueue_style()
{
  wp_enqueue_style('zenfse-style', get_stylesheet_directory_uri() . '/style.css', array(), filemtime(get_stylesheet_directory() . '/style.css'), false);
}
add_action('wp_enqueue_scripts', 'zenfse_enqueue_style');


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;However, if we need to enqueue parts of our CSS, such as resets, these won’t be loaded within the editor by default. Therefore, in my themes, I include a &lt;code&gt;dashboard-style.scss&lt;/code&gt; file to be loaded on the editor side through the &lt;code&gt;admin_enqueue_scripts action&lt;/code&gt;. This approach ensures that these styles are applied specifically within the WordPress editor.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

function zenfse_admin_styles()
{
  wp_enqueue_style('zenfse-admin-style', get_template_directory_uri() . '/dist/dashboard-style.css');
}
add_action('admin_enqueue_scripts', 'zenfse_admin_styles');


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;If I need to customize my dashboard, I can add specific styles to &lt;code&gt;dashboard-style.scss&lt;/code&gt;. To ensure our resets and styles don't impact WordPress's default styles, we should wrap our declarations within the &lt;code&gt;.wp-admin .editor-styles-wrapper&lt;/code&gt; selector. This confines our styles to apply only within the editor interface.&lt;/p&gt;

&lt;p&gt;As a final touch, we apply our global styles to the iframe within the editor. This approach ensures consistency in how our theme appears across different parts and templates modified within the WordPress dashboard.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

function zenfse_editor_style()
{
  add_editor_style(array('style.css',));
}

add_action('after_setup_theme', 'zenfse_editor_style');


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;End of digression.&lt;/p&gt;

&lt;h1&gt;
  
  
  The Toolbox
&lt;/h1&gt;

&lt;p&gt;We are operating within a Node environment, so our first step is to initialize our project by creating a &lt;code&gt;package.json&lt;/code&gt; where we define dependencies and commands. Personally, I use both &lt;a href="https://webpack.js.org/" rel="noopener noreferrer"&gt;webpack&lt;/a&gt; (on top of which &lt;a href="https://developer.wordpress.org/block-editor/reference-guides/packages/packages-scripts/" rel="noopener noreferrer"&gt;&lt;code&gt;@wordpress/scripts&lt;/code&gt;&lt;/a&gt; is built) and &lt;a href="https://parceljs.org/" rel="noopener noreferrer"&gt;Parcel&lt;/a&gt;. While using two different build engines may lack elegance, Parcel’s user-friendly approach compensates for this compared to webpack. Its commands are straightforward, requiring no additional configuration files. However, if you prefer using webpack exclusively, you can achieve this with just a few additions to your &lt;code&gt;webpack.config.js&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Within the &lt;code&gt;js&lt;/code&gt; and &lt;code&gt;css&lt;/code&gt; folders in &lt;code&gt;src&lt;/code&gt;, you can get creative with importing modules, exporting and importing functions, creating mixins, etc. In short, you have a modern development environment at your disposal, allowing you to tailor your workflow as needed.&lt;/p&gt;

&lt;h1&gt;
  
  
  The build process
&lt;/h1&gt;

&lt;p&gt;My &lt;code&gt;package.json&lt;/code&gt; looks something like this:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

{
  "name": "zenfse",
  "version": "1.3.0",
  "description": "blank fse developer theme",
  "scripts": {
    "dev:js": "parcel watch src/js/site.js",
    "dev:css": "sass --watch src/css/style.scss:style.css --style=expanded",
    "dev:css-dashboard-style": "sass --watch src/css/dashboard-style/dashboard-style.scss:dist/dashboard-style.css --style=expanded",
    "dev:wp-scripts-custom-blocks": "wp-scripts start --webpack-src-dir=blocks/custom-blocks/ --output-path=blocks/build-custom-blocks/",
    "dev": "concurrently --kill-others \"npm run dev:js\" \"npm run dev:css\" \"npm run dev:css-dashboard-style\" \"npm run dev:wp-scripts-core-blocks\" \"npm run dev:css-gsap-animations-frontend\" \"npm run dev:css-image-frontend\" \"npm run dev:css-image-backend\" \"npm run dev:css-image-style\" \"npm run dev:wp-scripts-custom-blocks\"",
    "clean": "rimraf ./blocks/build-custom-blocks ./dist",
    "build:js": "parcel build src/js/site.js --no-source-maps --no-content-hash",
    "build:css": "sass src/css/style.scss:style.css --style=compressed",
    "build:css-dashboard-style": "sass src/css/dashboard-style/dashboard-style.scss:dist/dashboard-style.css --style=compressed",
    "build:wp-scripts-custom-blocks": "NODE_ENV=production wp-scripts build --webpack-src-dir=blocks/custom-blocks/ --output-path=blocks/build-custom-blocks/",
    "build:css-autoprefixer": "postcss **/*.css --use autoprefixer --replace",
    "build": "npm run clean &amp;amp;&amp;amp; run-s build:*"
  },
  "author": "Silvia Malavasi",
  "license": "ISC",
  "browserslist": [
    "&amp;gt; 1%",
    "last 2 versions",
    "not ie &amp;lt;= 8"
  ],
  "devDependencies": {
    "@parcel/transformer-sass": "^2.10.3",
    "@wordpress/scripts": "^26.17.0",
    "parcel": "^2.10.3",
    "rimraf": "^5.0.5"
  },
  "dependencies": {
    "concurrently": "^8.2.2",
  }
}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;While &lt;code&gt;webpack.config.js&lt;/code&gt; will be simply:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

const defaultConfig = require("@wordpress/scripts/config/webpack.config");
module.exports = [defaultConfig];


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;When I run &lt;code&gt;npm run build&lt;/code&gt;, all of these commands will be executed:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

"clean": "rimraf ./blocks/build-custom-blocks ./dist",
"build:js": "parcel build src/js/site.js --no-source-maps --no-content-hash",
"build:css": "sass src/css/style.scss:style.css --style=compressed",
"build:css-dashboard-style": "sass src/css/dashboard-style/dashboard-style.scss:dist/dashboard-style.css --style=compressed",
"build:wp-scripts-custom-blocks": "NODE_ENV=production wp-scripts build --webpack-src-dir=blocks/custom-blocks/ --output-path=blocks/build-custom-blocks/",
"build:css-autoprefixer": "postcss **/*.css --use autoprefixer --replace",
"build": "npm run clean &amp;amp;&amp;amp; run-s build:*"


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;First, I clean up the project from the old build using &lt;a href="https://www.npmjs.com/package/rimraf" rel="noopener noreferrer"&gt;&lt;code&gt;rimraf&lt;/code&gt;&lt;/a&gt;. Then, I compile all my files using Parcel along with its &lt;a href="https://www.npmjs.com/package/@parcel/transformer-sass" rel="noopener noreferrer"&gt;&lt;code&gt;parcel/transformer-sass&lt;/code&gt;&lt;/a&gt;, plugin, and I process CSS using &lt;a href="https://www.npmjs.com/package/autoprefixer" rel="noopener noreferrer"&gt;&lt;code&gt;autoprefixer&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;wp-scripts&lt;/code&gt; handles the compilation of all my custom blocks based on the configuration specified in &lt;code&gt;webpack.config.js&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This build process setup is highly convenient because whenever I create a new custom block, I just need to place its folder in &lt;code&gt;blocks/custom-blocks&lt;/code&gt;, and it will be automatically compiled during the next &lt;code&gt;npm run build&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Credit for this approach goes to the &lt;a href="https://webberzone.com/" rel="noopener noreferrer"&gt;WebberZone&lt;/a&gt; article: “&lt;a href="https://webberzone.com/wordpress-block-development-building-multiple-blocks/" rel="noopener noreferrer"&gt;WordPress block development: Building multiple blocks.&lt;/a&gt;”&lt;/p&gt;

&lt;p&gt;Similarly, &lt;code&gt;npm run dev&lt;/code&gt; uses &lt;a href="https://www.npmjs.com/package/concurrently" rel="noopener noreferrer"&gt;&lt;code&gt;concurrently&lt;/code&gt;&lt;/a&gt; to manage all the various watch commands simultaneously.&lt;/p&gt;

&lt;h1&gt;
  
  
  Let’s import everything into functions.php
&lt;/h1&gt;

&lt;p&gt;Now that our files are ready, we can proceed with importing them into our &lt;code&gt;functions.php&lt;/code&gt;. We've already discussed some aspects related to styles. The complete process for importing global scripts and styles (which apply across the entire theme) is as follows:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

// Enqueue scripts

function zenfse_enqueue_script()
{
  wp_enqueue_script('zenfse-script', get_stylesheet_directory_uri() . '/dist/site.js', array(), filemtime(get_stylesheet_directory() . '/dist/site.js'), true);
}
add_action('wp_enqueue_scripts', 'zenfse_enqueue_script');

// Enqueue styles

function zenfse_enqueue_style()
{
  wp_enqueue_style('zenfse-style', get_stylesheet_directory_uri() . '/style.css', array(), filemtime(get_stylesheet_directory() . '/style.css'), false);
}
add_action('wp_enqueue_scripts', 'zenfse_enqueue_style');

// Enqueue Dashboard styles

function zenfse_admin_styles()
{
  wp_enqueue_style('zenfse-admin-style', get_template_directory_uri() . '/dist/dashboard-style.css');
}
add_action('admin_enqueue_scripts', 'zenfse_admin_styles');

// Enqueue Editor styles

function zenfse_editor_style()
{
  add_editor_style(
    array(
      'style.css',
    )
  );
}

add_action('after_setup_theme', 'zenfse_editor_style');


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Now let’s move on to importing our custom blocks, which we compiled into the directory &lt;code&gt;blocks/build-custom-blocks&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

function zenfse_register_blocks()
{

  add_filter('block_categories_all', 'zenfse_blocks_categories');
  function zenfse_blocks_categories($categories)
  {
    array_unshift($categories, array(
      'slug'  =&amp;gt; 'zenfse-blocks',
      'title' =&amp;gt; 'ZenFSE Blocks'
    ));
    return $categories;
  };


  $blocks = array(
    'navigation' =&amp;gt; 'zenfse_render_navigation_block',
    'gallery' =&amp;gt; '',
  );

  foreach ($blocks as $block =&amp;gt; $render_callback) {
    $args = array();
    if (!empty($render_callback)) {
      $args['render_callback'] = $render_callback;
    }
    register_block_type(__DIR__ . '/blocks/build-custom-blocks/' . $block, $args);
  }
}
add_action('init', 'zenfse_register_blocks');


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;In this example, I have imported two blocks: one is the &lt;code&gt;gallery&lt;/code&gt; block that we saw in &lt;a href="https://dev.to/silviamalavasi/lets-get-hands-on-with-wordpress-fse-theme-and-custom-blocks-part-1-lmj"&gt;Part 1 of the article&lt;/a&gt;. The other is a &lt;code&gt;navigation&lt;/code&gt; block, included here to illustrate its &lt;code&gt;render_callback&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Every time we want to add a new block, we simply add it to the &lt;code&gt;$blocks&lt;/code&gt; array, optionally with its callback function. It’s straightforward and efficient.&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion — for real
&lt;/h1&gt;

&lt;p&gt;We’ve delved into setting up a custom FSE theme in WordPress, organizing its structure, and managing the compilation process. Now, the next step is to innovate and create new blocks.&lt;/p&gt;

&lt;p&gt;While the process I’ve described may seem somewhat intricate, in a way, it is. Building my &lt;a href="https://github.com/SilviaMalavasi/zenfse_full" rel="noopener noreferrer"&gt;ZenFse&lt;/a&gt; starter theme involved a lengthy journey of trial and error.&lt;/p&gt;

&lt;p&gt;Initially, working with FSE left me feeling disoriented. The absence of PHP was unsettling. However, as I grasped the logic behind the block system and understood the unique integration between PHP and React, I discovered a powerful, versatile, and… enjoyable system. The capability to craft interfaces for nearly any attribute of my theme or blocks enables the development of intricate custom systems, fulfilling the visual builder objective of FSE in WordPress.&lt;/p&gt;

&lt;p&gt;Maintaining custom blocks in separate environments proves remarkably convenient. To replicate blocks from one theme to another, you simply copy a folder and add a line in the &lt;code&gt;$blocks&lt;/code&gt; array within &lt;code&gt;functions.php&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;From a developer’s viewpoint, once the theme is structured, we enjoy an intuitive environment. But what about users? Granted, every CMS demands some training for end users, but does FSE simplify matters? Yes and no. For existing WordPress users, starting anew can pose challenges. Moreover, the decision to develop a site using an FSE or classic theme depends on the client’s requirements.&lt;/p&gt;

&lt;p&gt;For example, if I’m constructing a site for an agency that requires precise control over colors, spacing, and enjoys experimenting with diverse block combinations on each project page, FSE is your choice. If a client wishes to incorporate an unexpected video or lengthy text into an existing block, they can experiment and preview the final outcome directly from the dashboard.&lt;/p&gt;

&lt;p&gt;Conversely, for a newbie needing a more structured system, I might opt for a classic theme, potentially integrating a series of fields with &lt;a href="https://www.advancedcustomfields.com/" rel="noopener noreferrer"&gt;ACF&lt;/a&gt; to limit theme flexibility.&lt;/p&gt;

&lt;p&gt;In conclusion, from my perspective, I find developing with FSE much more enjoyable. It feels cleaner and more elegant to me, though these preferences are subjective. Additionally, since FSE is still evolving, I’m excited to see what new tools the WordPress team will introduce in the future.&lt;/p&gt;

&lt;p&gt;Let’s also create a dedicated block category for our custom blocks, making it easier to locate and insert them into pages.&lt;/p&gt;

</description>
      <category>wordpress</category>
      <category>development</category>
      <category>fse</category>
      <category>blockthemes</category>
    </item>
    <item>
      <title>Let’s Get Hands-On with WordPress FSE Theme and Custom Blocks — Part 1</title>
      <dc:creator>Silvia Malavasi</dc:creator>
      <pubDate>Wed, 26 Jun 2024 17:37:56 +0000</pubDate>
      <link>https://dev.to/silviamalavasi/lets-get-hands-on-with-wordpress-fse-theme-and-custom-blocks-part-1-lmj</link>
      <guid>https://dev.to/silviamalavasi/lets-get-hands-on-with-wordpress-fse-theme-and-custom-blocks-part-1-lmj</guid>
      <description>&lt;h1&gt;
  
  
  FSE (Full Site Editing): Blessing and Curse
&lt;/h1&gt;

&lt;p&gt;The introduction of Full Site Editing (FSE) represents one of the most significant evolutions in WordPress history. The aim is to provide users with a visual builder-like experience, allowing them to see a direct preview in the dashboard of what will happen on the frontend.&lt;/p&gt;

&lt;p&gt;Since the beginning, the evolution of WordPress has pursued this need: to bridge the gap between the complexity of the CMS and the goal of creating a system where a moderately tech-savvy user can independently build a site suited to their needs, including the visual aspect.&lt;/p&gt;

&lt;p&gt;Like any evolutionary process, this journey has seen its share of rapid transformations. Sometimes, developers have found themselves facing an enormous change that took time to digest. With FSE and Block Themes in particular, the change has been radical:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No more PHP in the theme root.&lt;/li&gt;
&lt;li&gt;Strange HTML filled with comments that aren’t comments.&lt;/li&gt;
&lt;li&gt;A configuration file in JSON format.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For an introduction to these topics, &lt;a href="https://developer.wordpress.org/themes/"&gt;WordPress Developer Resources&lt;/a&gt; is a good starting point. &lt;a href="https://developer.wordpress.org/themes/core-concepts/theme-structure/"&gt;Here&lt;/a&gt;, the structure of the new themes is explained.&lt;/p&gt;

&lt;p&gt;A major innovation is the management of page parts. The header and footer can now be edited by users through a dedicated editor in the dashboard. Users can also visually manage templates in this editor, adding core blocks to each page template, including Query Loops. Templates and Parts are saved in the database but can be set via code using the strange HTML mentioned earlier.&lt;/p&gt;

&lt;p&gt;In terms of styling, an FSE theme maintains its own &lt;code&gt;style.css&lt;/code&gt;, while global styles for typography, colors, padding, and other site-wide elements are declared in the &lt;code&gt;theme.json&lt;/code&gt; file. These styles are accessible to users in the sidebar of the selected block.&lt;/p&gt;

&lt;p&gt;With these changes alone, it’s evident that the level of user control over the site is infinitely greater than in a traditional theme. Evolution has certainly taken place. However, as developers, we’re not just users; we want to delve deep into the possibilities that the Block Themes system offers. &lt;/p&gt;

&lt;p&gt;A notable innovation is an &lt;a href="https://developer.wordpress.org/themes/advanced-topics/build-process/"&gt;integrated build process&lt;/a&gt; in FSE. Do we want to add JavaScript in ES6? Do we want to use SASS? WordPress provides the WordPress scripts package (built on top of &lt;a href="https://webpack.js.org/"&gt;webpack&lt;/a&gt;) to compile and include our files.&lt;/p&gt;

&lt;h1&gt;
  
  
  Block Editor: It Gets Even Better
&lt;/h1&gt;

&lt;p&gt;Block Themes allow the integration of custom &lt;a href="https://developer.wordpress.org/block-editor/"&gt;Blocks&lt;/a&gt; that can be used anywhere in the theme. I’m not talking about &lt;a href="https://developer.wordpress.org/themes/patterns/"&gt;Patterns&lt;/a&gt;, which are essentially simplified versions of a Block designed to create a reusable graphic template (somewhat like the old reusable blocks). We’re talking about entities that use React and require a build process.&lt;/p&gt;

&lt;h2&gt;
  
  
  The anatomy of a block
&lt;/h2&gt;

&lt;p&gt;A block consists of several components. Some are for declaring the block, others for how the block behaves in the dashboard, and others for the block’s display on the frontend.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;index.js&lt;/code&gt; is our entry point and initializes the block&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;block.json&lt;/code&gt; declares the block properties, including custom attributes or additional JavaScript to be run on the frontend&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;edit.js&lt;/code&gt; and &lt;code&gt;edit.scss&lt;/code&gt; (or css) handle the dashboard-related part&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;save.js&lt;/code&gt; and &lt;code&gt;save.scss&lt;/code&gt; (or css) handle the frontend-related part&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Additional JavaScript or PHP files can be incorporated for specific functionalities, such as animating elements using GSAP or adjusting server-side rendering for the block.&lt;/p&gt;

&lt;p&gt;To use these blocks, they need to be registered in our &lt;code&gt;functions.php&lt;/code&gt; file.&lt;/p&gt;

&lt;h2&gt;
  
  
  Example block: gallery with SwiperJS
&lt;/h2&gt;

&lt;p&gt;Now, finally, some code. This block is moderately complex but perfect for explaining the integration between PHP and React. We will create a gallery with a management interface on the dashboard side. We’ll use &lt;a href="https://swiperjs.com/"&gt;SwiperJS&lt;/a&gt; library for the gallery’s JavaScript and import it into our block. The build process will be explained in the &lt;a href="https://dev.to/silviamalavasi/lets-get-hands-on-with-wordpress-fse-theme-and-custom-blocks-part-2-13ea"&gt;second part of the article&lt;/a&gt; because it differs somewhat from the standard process.&lt;/p&gt;

&lt;p&gt;Let’s start with &lt;code&gt;index.js&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { registerBlockType } from "@wordpress/blocks";
import "./edit.scss";
import "./save.scss";
import Edit from "./edit";
import save from "./save";
import metadata from "./block.json";
registerBlockType(metadata.name, {
  edit: Edit,
  save,
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nothing more than an entry point for the WordPress scripts package build system.&lt;/p&gt;

&lt;p&gt;In &lt;code&gt;block.json&lt;/code&gt;, we start to see something more interesting&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "apiVersion": 2,
  "name": "blocks/gallery",
  "title": "Gallery",
  "version": "1.0.0",
  "category": "custom-blocks",
  "icon": "format-gallery",
  "description": "A block with a gallery on the left, and text on the right.",
  "supports": {
    "html": false,
  },
  "editorScript": "file:./index.js",
  "editorStyle": "file:./index.css",
  "style": "file:./index.css",
  "viewScript": "file:./gallery.js",
  "attributes": {
    "slideCount": {
      "type": "number",
      "default": 1
    }
  },
  "example": {
    "innerBlocks": [
      {
        "name": "core/image",
        "attributes": {
          "url": "http://localhost/site/wp-content/themes/my-theme/blocks/custom-blocks/gallery/gallery.png",
          "alt": "gallery preview"
        }
      }
    ]
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In addition to the standard declarations, we have stated that we will use the &lt;code&gt;gallery.js&lt;/code&gt; file on the frontend.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"viewScript": "file:./gallery.js",
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And we have declared a new attribute called &lt;code&gt;slideCount&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"attributes": {
    "slideCount": {
      "type": "number",
      "default": 1
    }
  },
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;which we will use. We have also included a preview image to replace the standard preview system. This part is optional.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
"example": {
    "innerBlocks": [
      {
        "name": "core/image",
        "attributes": {
          "url": "http://localhost/site/wp-content/themes/my-theme/blocks/custom-blocks/gallery/gallery.png",
          "alt": "gallery preview"
        }
      }
    ]
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Alright, now React comes into play. Let’s look at edit.js It’s a bit long, but if you prefer, you can skip directly to the explanations below the code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Button } from "@wordpress/components";
import { useDispatch, useSelect } from "@wordpress/data";
import { InnerBlocks, useBlockProps } from "@wordpress/block-editor";
import { useEffect, useRef } from "@wordpress/element";
import Swiper from "swiper";
import { Navigation, Pagination } from "swiper/modules";

export default function Edit({ attributes, setAttributes, className, clientId }) {
  const ref = useRef();
  const swiperRef = useRef();
  const blockProps = useBlockProps({ ref });

  const { blockOrder, rootClientId, areBlocksInserted } = useSelect(
    (select) =&amp;gt; {
      const { getBlockOrder, getBlockHierarchyRootClientId } = select("core/block-editor");
      const blockOrder = getBlockOrder(clientId);
      const rootClientId = getBlockHierarchyRootClientId(clientId);
      return {
        blockOrder,
        rootClientId,
        areBlocksInserted: blockOrder.length === blockOrder.length,
      };
    },
    [clientId]
  );

  const { insertBlock, removeBlock } = useDispatch("core/block-editor");

  useEffect(() =&amp;gt; {
    if (
      areBlocksInserted &amp;amp;&amp;amp;
      ref.current &amp;amp;&amp;amp;
      !ref.current.querySelector(".block-editor-inner-blocks").classList.contains("swiper-container-initialized")
    ) {
      let swiperElement = ref.current.querySelector(".block-editor-inner-blocks");
      let swiperWrapper = ref.current.querySelector(".block-editor-block-list__layout");
      swiperElement.classList.add("swiper");
      swiperWrapper.classList.add("swiper-wrapper");
      let swiper_pagination = ref.current.querySelector(".swiper-pagination");
      let swiper_prev = ref.current.querySelector(".swiper-prev");
      let swiper_next = ref.current.querySelector(".swiper-next");

      swiperRef.current = new Swiper(swiperElement, {
        modules: [Navigation, Pagination],
        observer: true,
        observeParents: true,
        pagination: {
          el: swiper_pagination,
          clickable: true,
        },
        navigation: {
          nextEl: swiper_next,
          prevEl: swiper_prev,
        },
        slidesPerView: 1,
        speed: 800,
        touchStartPreventDefault: false,
      });
    }
  }, [areBlocksInserted, ref.current]);

  const TEMPLATE = [
    [
      "core/columns",
      { className: "gallery-cont swiper-slide" },
      [
        ["core/column", {}, [["core/image", {}]]],
        [
          "core/column",
          {},
          [
            ["core/heading", { placeholder: "Insert title", level: 2 }],
            ["core/heading", { placeholder: "Insert small title", level: 3 }],
            ["core/paragraph", { placeholder: "Insert content" }],
          ],
        ],
      ],
    ],
  ];

  const addSlide = () =&amp;gt; {
    const newSlideCount = attributes.slideCount + 1;
    setAttributes({ slideCount: newSlideCount });
    const slideBlock = wp.blocks.createBlock(
      "core/columns",
      { className: `gallery-cont swiper-slide slide-${attributes.slideCount}` },
      [
        wp.blocks.createBlock("core/column", {}, [wp.blocks.createBlock("core/image", {})]),
        wp.blocks.createBlock("core/column", {}, [
          wp.blocks.createBlock("core/heading", { placeholder: "Insert title", level: 2 }),
          wp.blocks.createBlock("core/heading", { placeholder: "Insert small title", level: 3 }),
          wp.blocks.createBlock("core/paragraph", { placeholder: "Insert content" }),
        ]),
      ]
    );
    insertBlock(slideBlock, blockOrder.length, clientId);
    swiperRef.current.update();
    swiperRef.current.slideTo(attributes.slideCount);

    setTimeout(() =&amp;gt; {
      swiperRef.current.slideTo(attributes.slideCount);
    }, 300);
  };

  const removeSlide = () =&amp;gt; {
    const newSlideCount = attributes.slideCount - 1;
    setAttributes({ slideCount: newSlideCount });
    if (swiperRef.current) {
      const activeIndex = swiperRef.current.activeIndex;
      const blockToRemove = blockOrder[activeIndex];
      removeBlock(blockToRemove, rootClientId);
      swiperRef.current.update();
    }
  };

  return (
    &amp;lt;div
      {...blockProps}
      className={`${className || ""} my-block gallery edit`}
    &amp;gt;
      &amp;lt;div className="swiper"&amp;gt;
        &amp;lt;div className="swiper-wrapper"&amp;gt;
          &amp;lt;InnerBlocks
            template={TEMPLATE}
            templateInsertUpdatesSelection={false}
            templateLock={false}
          /&amp;gt;
        &amp;lt;/div&amp;gt;
      &amp;lt;/div&amp;gt;
      &amp;lt;div className="swiper-pagination"&amp;gt;&amp;lt;/div&amp;gt;
      &amp;lt;div
        className="swiper-navigation"
        style={{ height: attributes.slideCount === 1 ? "0" : "50px" }}
      &amp;gt;
        &amp;lt;div className="swiper-prev"&amp;gt;&amp;lt;/div&amp;gt;
        &amp;lt;div className="swiper-next"&amp;gt;&amp;lt;/div&amp;gt;
      &amp;lt;/div&amp;gt;
      &amp;lt;div className="button-wrapper"&amp;gt;
        &amp;lt;Button
          isSecondary
          onClick={addSlide}
        &amp;gt;
          Add a Slide
        &amp;lt;/Button&amp;gt;
        &amp;lt;Button
          isSecondary
          onClick={removeSlide}
          style={{ display: attributes.slideCount &amp;gt; 1 ? "inline-flex" : "none" }}
        &amp;gt;
          Remove Current Slide
        &amp;lt;/Button&amp;gt;
      &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;First, let’s import some built-in WordPress functions&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Button } from "@wordpress/components";
import { useDispatch, useSelect } from "@wordpress/data";
import { InnerBlocks, useBlockProps } from "@wordpress/block-editor";
import { useEffect, useRef } from "@wordpress/element";
import Swiper from "swiper";
import { Navigation, Pagination } from "swiper/modules";
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Some of these are familiar to those who use React. Here we import a version of those functions that is usable in the editor.&lt;/p&gt;

&lt;p&gt;As attributes of &lt;code&gt;Edit&lt;/code&gt; function, we use, among others, our custom attribute that we declared in &lt;code&gt;block.json&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export default function Edit({ attributes, setAttributes, className, clientId }) {

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we set up &lt;code&gt;Refs&lt;/code&gt; in the React way and use &lt;code&gt;useDispatch&lt;/code&gt; and &lt;code&gt;useSelect&lt;/code&gt; to manage the dynamic data flow from the server.&lt;/p&gt;

&lt;p&gt;Inside &lt;code&gt;useEffect&lt;/code&gt;, we handle SwiperJS. This is intriguing because we are launching a gallery that will be displayed directly in the editor. If we optimize the block interface effectively, our user will see the gallery almost exactly as it will appear to site visitors (on the frontend). Of course, in the editor, we’ll also include buttons to add more slides, which won’t be visible to visitors. This marks a notable shift in perspective.&lt;/p&gt;

&lt;p&gt;Now, what does &lt;code&gt;TEMPLATE&lt;/code&gt; refer to?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const TEMPLATE = [
    [
      "core/columns",
      { className: "gallery-cont swiper-slide" },
      [
        ["core/column", {}, [["core/image", {}]]],
        [
          "core/column",
          {},
          [
            ["core/heading", { placeholder: "Insert title", level: 2 }],
            ["core/heading", { placeholder: "Insert small title", level: 3 }],
            ["core/paragraph", { placeholder: "Insert content" }],
          ],
        ],
      ],
    ],
  ];
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It’s a part of &lt;a href="https://developer.wordpress.org/block-editor/how-to-guides/block-tutorial/nested-blocks-inner-blocks/"&gt;InnerBlocks&lt;/a&gt;, a very handy system for importing elements already present in WordPress into our custom block, such as images (including the media library handling) and text elements. Optionally, we can insert videos, quotes, and many other blocks, each with their integrated management system. This syntax closely resembles what we find in the strange HTML of FSE themes, akin to what we see when copying a block and pasting it into a text file.&lt;/p&gt;

&lt;p&gt;The template will be rendered with minimal code in &lt;code&gt;save.js&lt;/code&gt;, while here it is invoked within &lt;code&gt;InnerBlocks&lt;/code&gt; in our return statement (yes, exactly, we're in React logic).&lt;/p&gt;

&lt;p&gt;The functions &lt;code&gt;addSlide&lt;/code&gt; and &lt;code&gt;removeSlide&lt;/code&gt; manage the insertion of new slides. The slide count is managed through the attribute we added. Why isn't it an internal variable? Because we need the slide count to be stored in the database, and that's exactly what custom attributes do. We use &lt;code&gt;createBlock&lt;/code&gt;, &lt;code&gt;insertBlock&lt;/code&gt;, and &lt;code&gt;removeBlock&lt;/code&gt; to insert and remove the slides, which are structured exactly like our initial &lt;code&gt;TEMPLATE&lt;/code&gt;. Let’s take a look:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const slideBlock = wp.blocks.createBlock(
      "core/columns",
      { className: `gallery-cont swiper-slide slide-${attributes.slideCount}` },
      [
        wp.blocks.createBlock("core/column", {}, [wp.blocks.createBlock("core/image", {})]),
        wp.blocks.createBlock("core/column", {}, [
          wp.blocks.createBlock("core/heading", { placeholder: "Insert title", level: 2 }),
          wp.blocks.createBlock("core/heading", { placeholder: "Insert small title", level: 3 }),
          wp.blocks.createBlock("core/paragraph", { placeholder: "Insert content" }),
        ]),
      ]
    );
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the end, our render function mirrors the HTML structure needed by SwiperJS, with our &lt;code&gt;TEMPLATE&lt;/code&gt; inserted as the first slide.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;InnerBlocks
  template={TEMPLATE}
  templateInsertUpdatesSelection={false}
  templateLock={false}
/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And we include buttons for inserting or removing slides:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;Button
  isSecondary
  onClick={addSlide}
  &amp;gt;
  Add a Slide
&amp;lt;/Button&amp;gt;
&amp;lt;Button
  isSecondary
  onClick={removeSlide}
  style={{ display: attributes.slideCount &amp;gt; 1 ? "inline-flex" : "none" }}
&amp;gt;
  Remmove Current Slide
&amp;lt;/Button&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The frontend part is much simpler. The file responsible for rendering our block on the site is &lt;code&gt;save.js&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { InnerBlocks, useBlockProps } from "@wordpress/block-editor";

export default function Save({ className }) {
  const blockProps = useBlockProps.save();

  return (
    &amp;lt;div
      {...blockProps}
      className={`${className || ""} my-block gallery`}
    &amp;gt;
      &amp;lt;div className="swiper"&amp;gt;
        &amp;lt;div className="swiper-wrapper"&amp;gt;
          &amp;lt;InnerBlocks.Content /&amp;gt;
        &amp;lt;/div&amp;gt;
      &amp;lt;/div&amp;gt;
      &amp;lt;div className="swiper-pagination"&amp;gt;&amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We import &lt;code&gt;InnerBlocks&lt;/code&gt; to display the content of our &lt;code&gt;InnerBlocks&lt;/code&gt; as defined in &lt;code&gt;edit.js&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Finally, we add the JavaScript code to launch the gallery with SwiperJS on the frontend in &lt;code&gt;gallery.js&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import Swiper from "swiper";
import { Pagination } from "swiper/modules";

function gallery() {
  document.querySelectorAll(".my-block.gallery").forEach(function (el) {
    var swiper_pagination = el.querySelector(".swiper-pagination");

    var swiper = new Swiper(el.querySelector(".swiper"), {
      modules: [Pagination],
      pagination: {
        el: swiper_pagination,
        clickable: true,
      },
      slidesPerView: 1,
      speed: 800,
    });
  });
}

document.addEventListener("DOMContentLoaded", gallery);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Create your own Blocks
&lt;/h1&gt;

&lt;p&gt;Creating your own blocks following this structure is straightforward at this point. Here, I’ve shown a block with a dynamic interface for managing slides, but you can apply the same method to create blocks that are simpler or much more complex. What remains to be defined is the folder structure of the project (whether it’s a theme or a plugin) and, of course, the build process. This will be the topic of the &lt;a href="https://dev.to/silviamalavasi/lets-get-hands-on-with-wordpress-fse-theme-and-custom-blocks-part-2-13ea"&gt;second part of the article&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Another function we can employ when we want to fetch dynamic data is &lt;a href="https://developer.wordpress.org/block-editor/how-to-guides/block-tutorial/creating-dynamic-blocks/"&gt;render_callback&lt;/a&gt;, which we pass as an argument when registering the block in &lt;code&gt;functions.php&lt;/code&gt;. &lt;code&gt;render_callback&lt;/code&gt; allows us to retrieve a list of posts, an archive, or navigation menus directly from the server. For instance, in my &lt;a href="https://github.com/SilviaMalavasi/zenfse_full"&gt;ZenFSE&lt;/a&gt; theme, I've developed a custom header that allows users to choose between two different menus—one for desktop and one for mobile. These menus are fetched using  &lt;code&gt;render_callback&lt;/code&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Even more: Modifying Core Blocks
&lt;/h1&gt;

&lt;p&gt;Now that we understand how a block works, keep in mind that WordPress Core Blocks operate in the same way (but with many more features). If you want to take a look at the source code of the blocks, you can go to the &lt;a href="https://github.com/WordPress/gutenberg/tree/trunk/packages/block-library/src"&gt;WordPress Repository&lt;/a&gt; on GitHub.&lt;/p&gt;

&lt;p&gt;All blocks, including Core blocks, can be customized using &lt;a href="https://developer.wordpress.org/block-editor/reference-guides/packages/packages-hooks/"&gt;hooks&lt;/a&gt;. In particular, we’re interested in &lt;code&gt;addFilter&lt;/code&gt;. With this hook, for example, we can enhance an existing block’s functionality without having to rewrite it from scratch. For instance, we could add an additional text field to an image block, or add a dropdown in the block sidebar for selecting an animation to execute when the block is rendered. In &lt;a href="https://github.com/SilviaMalavasi/zenfse_full"&gt;ZenFSE&lt;/a&gt;, I've used GSAP to add an entrance animation to all my blocks.&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion — Part One
&lt;/h1&gt;

&lt;p&gt;Conclusion — Part One&lt;br&gt;
This level of interactivity with components truly leaves ample room for creativity and imagination, both from the developer and the user perspectives. Another advantage is the modular nature of the code. Each block resides within its own folder with its files, and in terms of dependencies, each block has its own CSS and JavaScript. Blocks can be used anywhere on the site and can also be inserted into other blocks (such as groups or columns). Moreover, &lt;code&gt;InnerBlocks&lt;/code&gt; inherit the properties of the blocks they are composed of, so it’s possible to apply colors, typography, and all globally declared properties from the &lt;code&gt;theme.json&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;WordPress continues to evolve with new solutions, the latest of which, at the moment I am writing, is the &lt;a href="https://make.wordpress.org/core/2024/03/04/interactivity-api-dev-note/"&gt;Interactivity API&lt;/a&gt;, which allows defining block interactions in an even more immediate manner.&lt;/p&gt;

&lt;p&gt;There is no rest for developers.&lt;/p&gt;

</description>
      <category>wordpress</category>
      <category>development</category>
      <category>fse</category>
      <category>blockthemes</category>
    </item>
  </channel>
</rss>
