Three years ago, shipping a custom Gutenberg block meant configuring webpack, setting up the wp-scripts toolchain, and managing a build/ directory you would never touch again. In 2026 you can skip all of it. Here is the build-tool-free workflow we use for client-specific custom blocks.
What changed
Three things made this possible:
block.jsonsupport is stable. WordPress reads metadata directly fromblock.jsonand registers your block from PHP. No JS bundle required for blocks that do not need a custom editor UI.- Dynamic blocks via
render_callback. If your block is server-rendered, you write a PHP function. Editor preview can use the same render. - Iframe-isolated editor. Means inline
<style>and small inline scripts work cleanly inside the block without leaking into the surrounding editor.
The pattern
// my-block/block.json
{
"apiVersion": 3,
"name": "wpt/team-card",
"title": "Team Card",
"category": "design",
"attributes": {
"name": { "type": "string" },
"role": { "type": "string" },
"photo": { "type": "string" }
},
"render": "file:./render.php"
}
Then in render.php you return the front-end markup. Register it in your theme or a small plugin with a single PHP call:
add_action('init', function () {
register_block_type(__DIR__ . '/my-block');
});
That is the entire setup. No webpack, no JS bundle, no npm install.
When you still need JS
If your block needs a richer editor UI — sliders, repeater fields, custom controls — you still need a small JS file with registerBlockType. But for the “data in, HTML out” case that covers maybe 70 percent of custom blocks we ship, the JS is not needed at all.
The honest tradeoff
The build-tool-free pattern is faster to ship and easier to maintain, but you lose the polished editor preview. Editors see a placeholder with the attributes; clients sometimes prefer to see the real rendered block in the editor. Decide per-block whether that matters.
What to try this afternoon
Pick the simplest custom block you have shipped recently — the one you would describe as “just a div with some content fields”. Re-implement it as a server-rendered block with block.json + render.php and no JS. You will probably save 80 percent of the code and all of the build complexity.
