The Gutenberg editor has revolutionized how content is created in WordPress. While simple blocks can be built quickly, creating complex, dynamic blocks with React and server-side rendering (SSR) unlocks powerful capabilities — especially when dealing with dynamic data or API integrations.
In this guide, we’ll cover how to build an advanced Gutenberg block using:
- React (JSX + Hooks)
@wordpress/scripts
for toolingregister_block_type
withrender_callback
- Inspector controls
- Dynamic attributes and server rendering
Prerequisites
Before starting, make sure you have:
- A working WordPress plugin or theme
- Node.js and npm installed
@wordpress/scripts
setup for building your block
You can scaffold a block using:
npx @wordpress/create-block my-dynamic-block
Project Structure
Once scaffolded, your plugin folder will look like this:
my-dynamic-block/
├── src/
│ ├── edit.js // React editor UI
│ ├── save.js // Can be skipped for dynamic blocks
│ └── index.js // Block registration
├── dynamic-block.php // Server-side rendering logic
└── block.json // Block metadata
Why Dynamic Rendering?
Dynamic rendering allows your block to:
- Display live data (e.g. latest posts, user info, API content)
- Render server-side for SEO and performance
- Avoid bloated frontend JS for static rendering
Example Use Case: Recent Posts Grid
We’ll create a custom block that:
- Displays recent posts in a grid
- Lets you control the number of posts in the sidebar
- Dynamically renders via PHP on the frontend
Step 1: block.json
Setup
{
"apiVersion": 2,
"name": "myplugin/recent-posts-grid",
"title": "Recent Posts Grid",
"category": "widgets",
"icon": "grid-view",
"supports": {
"html": false
},
"attributes": {
"postCount": {
"type": "number",
"default": 3
}
},
"editorScript": "file:./index.js",
"render": "file:./dynamic-block.php"
}
Step 2: React Editor UI (edit.js
)
import { InspectorControls } from '@wordpress/block-editor';
import { PanelBody, RangeControl } from '@wordpress/components';
export default function Edit({ attributes, setAttributes }) {
return (
<>
<InspectorControls>
<PanelBody title="Block Settings">
<RangeControl
label="Number of posts"
value={attributes.postCount}
onChange={(value) => setAttributes({ postCount: value })}
min={1}
max={10}
/>
</PanelBody>
</InspectorControls>
<p><em>Recent posts will be shown on the frontend.</em></p>
</>
);
}
Step 3: Server-Side Render (dynamic-block.php
)
function render_recent_posts_grid( $attributes ) {
$query = new WP_Query([
'posts_per_page' => intval($attributes['postCount']),
]);
if ( ! $query->have_posts() ) {
return '<p>No recent posts found.</p>';
}
ob_start();
echo '<div class="recent-posts-grid">';
while ( $query->have_posts() ) {
$query->the_post();
echo '<div class="post-item">';
echo '<h4>' . esc_html(get_the_title()) . '</h4>';
echo '<a href="' . esc_url(get_permalink()) . '">Read More</a>';
echo '</div>';
}
echo '</div>';
wp_reset_postdata();
return ob_get_clean();
}
register_block_type( __DIR__, [
'render_callback' => 'render_recent_posts_grid'
]);
Bonus Tips
- Add styles using
editorStyle
andstyle
inblock.json
- Use
useSelect()
in the editor to preview post data live - Use
InnerBlocks
to allow nested block customization - Use
withSelect
HOC for backward compatibility if needed
Final Thoughts
Creating dynamic Gutenberg blocks with React gives you the best of both worlds:
- A modern editing experience
- Dynamic, SEO-friendly output
- Clean separation between editor and render logic
With this pattern, you can build anything from live charts, API feeds, product listings, or custom dashboards — all powered by WordPress.