Recently, I was tasked with implementing server-side rendering for a WordPress block created with @wordpress/create-block
package. Specifically, I developed a block that displays a page list based on a user-selected menu.
The block’s functionality is straightforward: in the back-end, the user selects a menu from a list of available menus, and on the front-end, the selected menu is displayed dynamically as standard wordpress menu.
While working on this, I experimented with several approaches before realizing that this functionality has been natively supported in WordPress since version 6.1 (release on November 1, 2022). Here’s the relevant code from the WordPress source:
if ( ! empty( $metadata['render'] ) ) {
$template_path = wp_normalize_path(
realpath(
dirname( $metadata['file'] ) . '/' .
remove_block_asset_path_prefix( $metadata['render'] )
)
);
if ( $template_path ) {
/**
* Renders the block on the server.
*
* @since 6.1.0
*
* @param array $attributes Block attributes.
* @param string $content Block default content.
* @param WP_Block $block Block instance.
*
* @return string Returns the block content.
*/
$settings['render_callback'] = static function ( $attributes, $content, $block ) use ( $template_path ) {
ob_start();
require $template_path;
return ob_get_clean();
};
}
}
This snippet is from the register_block_type_from_metadata function, which handles parsing the block.json
definition. To define a custom PHP renderer, simply include it in your block definition like this:
{
"$schema": "https://schemas.wp.org/trunk/block.json",
"apiVersion": 3,
"name": "theme/menu-items-list",
"version": "0.1.0",
"title": "Menu Items List",
"category": "widgets",
"attributes": {
"menuId": {
"type": "string",
"default": "0"
}
},
"icon": "smiley",
"description": "Display menu list.",
"example": {},
"supports": {
"html": false
},
"textdomain": "menu-items-list",
"editorScript": "file:./index.js",
"editorStyle": "file:./index.css",
"style": "file:./style-index.css",
"viewScript": "file:./view.js",
"render": "file:./render.php"
}
Place the render.php
file in the same directory as your block.json
file.
The ./render.php
file will be rendered on the user-facing front end (not in the admin interface). In this file, you also have access to the $attributes
, $content
, and $block
variables, allowing you to customize the output dynamically based on block settings and content.
I think that is pretty neat feature for WordPress developers.
Here are my contents for the PHP file:
<?php
$menu_id = $attributes['menuId'] ?? null;
$menu_items = wp_get_nav_menu_items($menu_id);
if (!$menu_id || !$menu_items) {
if (!$menu_items) {
echo '<p>' . esc_html__('No menu selected.', 'menu-items-list') . '</p>';
} else if (!$menu_items) {
echo '<p>' . esc_html__('No menu items found for the selected menu.', 'menu-items-list') . '</p>';
}
} else {
wp_nav_menu(array(
'menu' => $menu_id, // Replace '3' with the ID of your menu
'theme_location' => '', // Leave empty if using 'menu' ID
'container' => 'div', // The container HTML element (default: 'div')
'container_class' => 'menu-container', // Class for the container
'menu_class' => 'menu', // Class for the ul element
'fallback_cb' => false // Callback if the menu does not exist
));
}