How Advanced Custom Fields handle blocks

Hi there!

One main discussion we are having in the WordPress ecosystem right now is how we approach the block structure. With the “Dynamic Blocks” appearing, we got a mixed approach of both React and PHP, adding server-side capabilities to them, but also we added a more difficult stack of technologies to the system (you know need both PHP and React in order to create a block).

As commented in many places, this is a way you can change markups on all blocks of a site without the danger of crashing any block.

There was a comment that started the discussion:

I really don’t care whether a block is rendered with JS/React or PHP or whatever, but what I care about is that the block’s markup is written in a well structured templating language. The blocks data is passed to the template and the template consumes it.

Therefore until now I mainly use ACF to build my blocks and PHP/Twig to render them. This gives me the freedom to override a block’s markup in every child theme I create and output a totally different design based on the same data. Being able to override a block’s markup easily and dynamically (without the need of re-saving it) is crucial for my development workflow with ever changing designs for the same data.

This approach mentioned is just using the block editor in order to save attributes and a markup language to create the ‘view’. In order to understand more about this solution, I checked how to create blocks using the ACF plugin.

First, define the block in other plugin or theme.

In order to register a block, you have to add a function to your plugin or theme.

add_action('acf/init', 'my_acf_init_block_types');
function my_acf_init_block_types() {

    // Check function exists.
    if( function_exists('acf_register_block_type') ) {

        // register a testimonial block.
            'name'              => 'testimonial',
            'title'             => __('Testimonial'),
            'description'       => __('A custom testimonial block.'),
            'render_template'   => 'template-parts/blocks/testimonial/testimonial.php',
            'category'          => 'formatting',
            'icon'              => 'admin-comments',
            'keywords'          => array( 'testimonial', 'quote' ),

This approach is quite easy for WordPress developers, as is a really common approach for most plugin-theme development. You can do it in a custom plugin, in a inject code plugin, in a theme or in a child theme.

Once you have registered the block, it will appear when you create the different fields that the block will have, for that, you have the common ACF entire field suite.

An example of some fields created for the block
An example of some fields created for the block

Once you have updated the fields you want for that block, you can go to the editor to take a look at it, we have not defined yet the markup, so nothing will be shown apart from the sidebar fields.

Custom fields applied on the right side
Custom fields applied on the right side

So, in order for the block to be shown both in the editor and frontend, we have to create a template-part inside a theme with this content:


 * Test Block Template.
 * @param   array $block The block settings and attributes.
 * @param   string $content The block inner HTML (empty).
 * @param   bool $is_preview True during AJAX preview.
 * @param   (int|string) $post_id The post ID this block is saved to.

// Create id attribute allowing for custom "anchor" value.
$id = 'block-test-' . $block['id'];
if ( ! empty( $block['anchor'] ) ) {
	$id = $block['anchor'];

// Create class attribute allowing for custom "className" and "align" values.
$className = 'block-test-';
if ( ! empty( $block['className'] ) ) {
	$className .= ' ' . $block['className'];
if ( ! empty( $block['align'] ) ) {
	$className .= ' align' . $block['align'];

// Load values and assign defaults.
$label            = get_field( 'label' ) ?: 'Label';
$counter          = get_field( 'counter' ) ?: 'Counter';
$background_color = get_field( 'background_color' );


<div id="<?php echo esc_attr( $id ); ?>" class="<?php echo esc_attr( $className ); ?>">
	<div class="block-test">
		<label><?php echo esc_html( $label ); ?></label>
		<div class="counter" id="counter"><?php echo $counter; ?></div>
	<style type="text/css">
		#<?php echo $id; ?> {
			background: <?php echo $background_color; ?>;
	<script type="text/javascript">
	var counter = document.getElementById("counter");
	counter.addEventListener('click', function() {

To sum up, you can create variables with the function get_field and then they are printed on the code. The markup created is up to the developer, and you can add styles, scripts, whatever you want. It follows a very well-known method for all WordPress developers, as is quite similar to theme development.

After that, you have the editor as the screenshot

Editor showing your ACF block
Editor showing your ACF block

This is the markup created on editor:

HTML created on editor
HTML created on editor

This is how its seen on the frontend:

Frontend showing your ACF block
Frontend showing your ACF block

And finally, the markup on the frontend feels like this, the same as defined on the PHP file:

Markup on the frontend

A summary of this approach

This method to create blocks has some benefits and also some issues

The benefits I found are:

  • React or JS is not needed. This means that the method is really friendly with the main PHP developers that work with WordPress.
  • The interface for custom fields is quite complete. The options are a lot, including color selectors, numbers, text, textarea, calendar, etc. This plugin converts WordPress into a full CMS system, allowing to create unlimited fields for different post types. Is just my opinion, but custom fields have never been really friendly in WordPress core by default.
  • As all blocks created this way are rendered by the server, there are no issues with block deprecation, also, it is not saving on the DB any markup, like the most recent dynamic blocks. I don’t have the knowledge to determine if not saving any HTML on the DB is good or not for performance on high traffic sites.

Some issues are:

  • You lose the capabilties of editing the block in other part that is not the sidebar option. This means that you are only filling fields on the sidebar, instead of writing text on the editor. For example, this approach, for paragraphs, it would be weird to write the content in a small sidebar instead of the main block.
  • If you want a lot of customization, for example font-size, font color, font background, font family, border options, etc. You have to create a ton of custom fields, and also a ton of PHP code. The current approach of block attributes is far from perfect, but still it gathers all the styling in an easy and fast way. I think the current approach is way better in terms on the sidebar size and user experience.
  • Is not compatible with full site editing approach, at least as far as I know, it needs the old template system to work. The plugin focus on the page/post content. I don’t know how they will connect the plugin to full site editing or move this block system to every part of the site.
  • I didn’t try to create Innerblocks or more difficult ones. There are some posts that explain how to do it.

What do you think about this solution?

Feel free to add comments, suggest changes, or discuss this! There is a discussion available on Github, and, if you find links about these discussions, feel free also to include them in the comments section so I will include them on this list!

One response to “How Advanced Custom Fields handle blocks”

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: