Custom Gutenberg Blocks: When to Build and How Long It Takes
Should you build a custom Gutenberg block or use ACF? A native block vs ACF block comparison, real effort ranges, and the four scenarios that justify the cost.
Should you build a custom Gutenberg block or use ACF? For most agency briefs, the honest answer is ACF. An ACF block produces the same visible result on the page as a native block for roughly a quarter of the build effort, and the client almost never sees the difference. A native Gutenberg block, the kind defined by a block.json file and a React component with a real build step, is the more impressive piece of engineering, but impressive engineering that nobody asked for is just an inflated invoice.
That does not mean native blocks are never worth it. They are, in four specific situations: when the block is reused across many sites, when it needs a genuinely rich in-editor experience, when it sits on a performance-critical path, and when it is part of a theme or plugin you ship to other people. This article walks through how each type is actually built, what each one costs in developer days, and how to tell which of the two a given block deserves. If your block does not clear at least one of those four bars, the ACF block is not the lazy choice. It is the professional one.
Two kinds of "custom block" that get conflated
When a client asks for a custom Gutenberg block, they almost always mean "a reusable content element my editors can drop into a page." That outcome can be delivered two completely different ways, and the WordPress community uses the word "block" for both, which is where the confusion starts.
A native block is the core WordPress way. It is defined by a block.json metadata file, and its editing interface is a React component you write. WordPress reads block.json from both PHP and JavaScript, so it is the single source of truth for the block's attributes, its supported features, and which scripts and styles it loads. The block can save static HTML directly, or it can be dynamic and render its output through a PHP file at request time. Either way, the editing experience lives on the canvas itself.
An ACF block is the Advanced Custom Fields way. The developer registers a block, attaches an ACF field group to it, and writes one PHP template that renders the block's markup. That same template is used to draw the editor preview and the front-end output. The editing interface is whatever fields the field group contains, shown in the inspector sidebar. There is no React, no build step, and no compilation. Since ACF 6.0 these blocks can also use a block.json file with an acf configuration key, but underneath, the rendering is still plain PHP.
Both approaches register a real Gutenberg block. Both appear in the inserter. Both can be styled, locked, and templated. The difference is entirely in how the block is built and edited, not in what the page visitor receives.
How a native block is built and what it costs
A native block starts with a build pipeline. The standard tool is @wordpress/scripts, usually scaffolded with npx @wordpress/create-block, which generates the folder, the npm scripts, and a working webpack configuration that compiles JSX and modern JavaScript down to something browsers run. The heart of the block is its block.json file:
{
"$schema": "https://schemas.wp.org/trunk/block.json",
"apiVersion": 3,
"name": "witscode/stat-counter",
"title": "Stat Counter",
"category": "widgets",
"attributes": {
"value": { "type": "string", "default": "0" },
"label": { "type": "string" }
},
"supports": { "color": true, "spacing": { "padding": true } },
"editorScript": "file:./index.js",
"style": "file:./style-index.css",
"render": "file:./render.php"
}
Registration on the PHP side is a single line, because register_block_type reads everything it needs from that file:
add_action( 'init', function () {
register_block_type( __DIR__ . '/build/stat-counter' );
} );
The editing interface lives in an edit.js React component. It uses core components like RichText for inline editable text, InspectorControls for the sidebar panels, and InnerBlocks for nested content. The reward for this work is full editor parity. The block on the canvas behaves like the block on the front end, editors manipulate it directly rather than filling in a side panel, and you get block supports for colour, spacing, typography, and borders almost for free by listing them in supports.
The cost is real and worth stating in plain numbers. A simple static native block, something with a RichText field and a couple of inspector controls, is one to two developer days once the toolchain exists, plus roughly half a day the first time you stand up the build pipeline on a fresh project. A dynamic block that renders server-side, pulls from a custom data source, and ships its own front-end script runs three to five days. A native block with a genuinely rich editor experience, meaning nested InnerBlocks, custom inspector panels, block variations, transforms from other blocks, and an Interactivity API script for front-end behaviour, is comfortably one to two weeks. There is also a maintenance tax. Native blocks track the @wordpress/* package versions, and when saved markup changes you sometimes have to write block deprecations so older content does not break.
How an ACF block is built and what it costs
An ACF block skips the entire toolchain. There is no npm, no webpack, and no compilation. The developer registers the block, either through acf_register_block_type or through a block.json file with an acf key pointing at a render template, then creates a field group in the ACF interface and binds it to that block.
add_action( 'acf/init', function () {
acf_register_block_type( array(
'name' => 'stat-counter',
'title' => 'Stat Counter',
'render_template' => 'blocks/stat-counter.php',
'category' => 'widgets',
'mode' => 'preview',
) );
} );
The render template is ordinary PHP. It reads the field values and prints markup, and that exact same file draws the editor preview and the live page:
<div class="stat-counter">
<span class="stat-counter__value"><?php echo esc_html( get_field( 'value' ) ); ?></span>
<span class="stat-counter__label"><?php echo esc_html( get_field( 'label' ) ); ?></span>
</div>
That is the whole block. The editing interface is the field group, shown in the inspector sidebar, and the editor configures it the same way they would configure any ACF field anywhere else in WordPress.
The cost picture is much lighter. A simple ACF block, a handful of fields and one render template, is half a day to a full day, most of which is field setup and CSS rather than logic. A complex one, with flexible content layouts, conditional fields, and repeaters, is one to three days. Maintenance is close to zero, because the rendering is plain PHP and plain PHP does not break when a JavaScript package publishes a major version.
The trade-off is the editing experience ceiling. ACF blocks edit through fields in a sidebar, not through direct manipulation on the canvas. For a card with a title, an image, and a link, nobody will ever notice or care. For a block that needs inline rich text across several nested regions, drag-and-drop arrangement, or live colour and spacing feedback as the editor works, the sidebar model runs out of room. There is also a dependency to weigh. ACF Pro is a paid plugin, owned by WP Engine since 2022, although the Secure Custom Fields fork now lives in the wordpress.org repository as a free drop-in replacement.
Should you build a native block or an ACF block?
Here is the decision rule WitsCode applies before writing a line of code. Default to an ACF block. Then check the brief against four bars. If the block clears at least one of them, build it native. If it clears none, the ACF block is the correct answer and the conversation is over.
The first bar is reuse across many sites. A native block can live in a shared private package and be installed everywhere with the same versioned code. An ACF block is wired to a specific field group, which makes it harder to port cleanly between projects. When the same block will appear on dozens of sites, the higher native build cost amortises across all of them, and a shared block library becomes a genuine company asset rather than a per-project expense. For an agency running hundreds of sites, that maths flips quickly.
The second bar is rich editor experience. If editors have to manipulate the block directly on the canvas, with inline rich text in more than one region, nested InnerBlocks, live visual feedback on colour and spacing, block variations, or transforms from other block types, only a native block can deliver it. The ACF sidebar-fields model simply does not reach that far. If the editing model is "fill in these fields," ACF is fine. If the editing model is "design on the canvas," it is not.
The third bar is a performance-critical path. A native static block saves plain HTML into post content. No render callback runs when the page is served, no PHP executes for that block, and unless you deliberately opt into a front-end script, no JavaScript ships either. An ACF block always runs a PHP render callback on every request. For most blocks this difference is irrelevant noise. For a block on a hot path, such as an above-the-fold hero on a high-traffic template, the native static block is measurably the lighter choice.
The fourth bar is shipping the block as a product. If the block is part of a theme or plugin you distribute to other people, a hard dependency on ACF Pro is a non-starter, because your users may not own it. Native blocks are dependency-free and distributable, and a block.json-defined block is exactly what the Block Directory and the theme review process expect to see. Anything you sell or give away should be native for that reason alone.
If none of the four apply, and for a large share of ordinary site builds none of them do, the ACF block wins on every axis that matters: faster to build, cheaper to maintain, lower skill floor, and invisible to the visitor. Choosing it is not cutting a corner. It is matching the tool to the job.
A worked example
Consider a logo wall, a row of client logos that an editor updates every few months. It needs no canvas interactivity, it is not on a performance-sensitive path, it is specific to one site, and it is not being sold. It clears zero bars. Build it as an ACF block with a repeater field, ship it in a day, and never think about it again.
Now consider a filtered case-study grid that appears across forty client sites, lets editors pick layout variations on the canvas, lazy-loads results with an Interactivity API script, and is part of the agency's standard block library. It clears reuse, rich editor experience, and arguably performance. That one is native, it belongs in the shared package, and the one-to-two-week build is justified because every future project draws on it for free.
The two blocks look similar in a brief. They are not similar projects, and the four-bar test is what tells them apart before the estimate is written.
Where WitsCode lands on this
Across the 250-plus WordPress sites WitsCode maintains, the split is roughly what this article recommends. Most one-off, site-specific content elements are ACF blocks, because they are quick to build, trivial to maintain, and indistinguishable to the people using the site. The blocks that justify a native build, the ones that recur across many client projects or demand a real on-canvas editing experience, live in a shared, versioned native block library that every new project inherits. That library is why a native build is economical for us even when it would not be economical for a single site.
If you are weighing a custom Gutenberg block, the most valuable thing an agency can do is tell you honestly which of the two you actually need, rather than defaulting to the more expensive one because it looks more like real engineering. WitsCode builds both, and the recommendation always starts with the four-bar test, not the invoice. If you want a custom block built, or a shared block library that pays for itself across a portfolio of sites, that is the work we do.
Get weekly field notes.
Practical writing on shipping products, straight to your inbox. No spam.
Need help with this?
WordPress Development
We design and build web apps, MVPs, and SaaS products. Talk to us about what you are working on.
Talk to usWant to discuss headless / custom / advanced wp for your business?
Start a project and we'll talk through where you are, what's working, and the highest-leverage moves for the next 90 days.