ACF Gutenberg Blocks

No React, No Problem

Advanced Custom Fields now offers a simple way of converting traditional ACF field groups into Gutenberg blocks without writing a line of React. This exciting announcement from the ACF team is really appealing if you aren’t ready to dive head-first into building custom Gutenberg blocks, which can be a time consuming and daunting prospect.

This capability in ACF is only available in 5.8.0 beta1 with a Pro account, but hopefully rolls out shortly after the release of Gutenberg and WordPress 5.0, currently slated for November 19th. With that in mind, this post will cover a first attempt at converting our already-built ACF field groups into Advanced Custom Blocks (Guten Custom Fields?).

Create a Field Group

Set up an ACF field group as you normally would. For this example we will create a simple testimonial block.

Register the Block

In functions.php, register your block and hook into the acf/init action. Easy enough, but my block would not register at first. Turns out I had defined more than 3 keywords, which WordPress didn’t like.

function kd_acf_init() {
	if(function_exists('acf_register_block')) {
			'name' => 'testimonial',
			'title' => __('Testimonial'),
			'description' => __('An ACF Testimonial Block'),
			'render_callback' => 'acf_block_callback',
			'category' => 'common',
			'icon' => 'admin-comments',
			'keywords' => array('testimonial', 'quote', 'acf'),
add_action('acf/init', 'kd_acf_init');

Render the Block

Now we need to include our template file containing the testimonial’s markup and field values. We will do this within our callback function passed into acf_register_block in the previous step.

function acf_block_callback($block) {
	// convert name ("acf/testimonial") into path friendly slug ("testimonial")
	$slug = str_replace('acf/', '', $block['name']);

	// include a template part from within the "template-parts/block" folder
	if( file_exists(STYLESHEETPATH . "/components/blocks/block-{$slug}.php") ) {
		include( STYLESHEETPATH . "/components/blocks/block-{$slug}.php" );

The template file, block-testimonial.php, contains the code we would use with regular old ACF, the only difference being the Block Name declaration at the top.

 * Block Name: Testimonial

	$cite_field = get_field('source_url');

	if($cite_field) {
		$cite = 'cite="' . get_field('source_url') . '"';
	} else {
		$cite = '';

<section class="cs-testimonial container-site">
	<div class="cs-block-offset ">
		<div class="quote">
			<blockquote cite="<?php echo $cite; ?>">
				<p class="heading-4">“<?php the_field('testimonial'); ?>”</p>
		<?php if(get_field('source')) : ?>
			<div class="source">
				<cite><?php the_field('source'); ?></cite>
		<?php endif; ?>

That’s it! Now when I go back into the post, the testimonial block is available to be selected. Here is a side by side comparison of what our traditional testimonial field group looks like, compared to our new custom block.

Old school ACF field group compared to an ACF Block (on right)

The testimonial rendered out on the front-end as expected, so let’s move on to converting a relationship field. This is arguably the most useful ACF field, so being able to achieve the same functionality in this new Gutenberg world is critical for our development team.

Converting an ACF Relationship block

After creating our traditional relationship field group, we call the acf_register_block function again, with our block’s name, title, etc.

acf_register_block( array(
			'name' => 'relationship',
			'title' => __('Relationship'),
			'description' => __('An ACF Relationship Block'),
			'render_callback' => 'acf_block_callback',
			'category' => 'common',
			'icon' => 'admin-comments',
			'keywords' => array('relationship', 'posts', 'gcf'),

Now we just need to make sure the block-relationship.php template file is in the proper directory, along with the code used to output a traditional relationship field. The only addition was needing to invoke the WordPress global $post object at the top of the template.

 * Block Name: Relationship

	global $post;
	$posts = get_field('gcf_relationship');

	if($posts) :
	<section class="relationship-example">
		<?php foreach($posts as $post) : setup_postdata($post); ?>
			<a href="<?php the_permalink(); ?>">
				<span><?php the_field('name'); ?></span>
		<?php endforeach; wp_reset_postdata(); ?>

<?php endif; ?>

And that’s all it takes to create a ACF relationship block!

Where’s the Data?

WordPress has always saved post content in the wp_post table, and custom fields in the wp_postmeta table – and that won’t change with Gutenberg. How it is saved does. This is an example of how the core Gutenberg paragraph block saves in the database – the comments enable a post to switch back to the classic editor without breaking.

<!-- wp:paragraph -->
<p>WordPress has always saved post content in the <code>wp_post</code> table, and custom fields in the <code>wp_postmeta</code> table - and that won't change with Gutenberg. How it is saved does. This is an example of how the core Gutenberg paragraph block saves in the database - the comments enable a post to switch back to the classic editor without breaking.</p>
<!-- /wp:paragraph -->

Here is how ACF saves data in this context:

<!-- wp:acf/relationship {"id":"block_5bc8c2924b0ed","data":{"field_5bc8a916e996a":["59","1655"]},"name":"acf/relationship"} /-->

The id references the field group’s slug, and data it’s key value pair. So the content is still stored in wp_postmeta but is referenced in the post_content column in the wp_posts table. This allows developers access to custom field data throughout a site just like the pre-Gutenberg days, while seamlessly fitting into the new post editing experience.

What’s Next?

This initial look at how to incorporate ACF into Gutenberg, was very encouraging! The time spent converting field groups into blocks was a fraction of what it takes to build a custom Gutenberg block in React. And the familiarity of ACF will make the transition to Gutenberg smoother for a lot of developers.

Posted By

Posted on

Have a project
to discuss?

Let's see if we can help.

Start a Conversation