Custom Gutenberg Blocks Without Build Tools: The 2026 Way

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.json support is stable. WordPress reads metadata directly from block.json and 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.