Custom Gutenberg blocks with ACF Pro
written by: Jeff McNearCustom Gutenberg blocks with ACF Pro
I should premise this article with the fact that I install Advanced Custom Fields Pro with almost every site I build. The free version of ACF is very powerful, but at least as of right now (6/5/2020) the ability to create a custom Gutenberg block requires the Pro version. If I weren’t already installing ACF I may take a closer look at the Block Lab plugin (https://wordpress.org/plugins/block-lab/) which facilitates the building of custom plugins in possibly an easier manner.
As of about a year ago ACF Pro includes an option to apply the “location” rule of an ACF group to a block

But if the block hasn’t been registered somewhere (in your theme or plugin) ACF has no block to find.

REGISTERING THE BLOCK
Similar to registering a post type in WP, the acf_register_block() function allows you to register a custom block type from your functions.php file. This function accepts an array of settings that you can use to customize your block including a name, description and more.
(added to the functions file of your active theme)
add_action('acf/init', 'my_acf_init');
function my_acf_init() {
// check function exists
if( function_exists('acf_register_block') ) {
acf_register_block(array(
'name' => 'accordion',
'title' => __('Accordion'),
'description' => __('A custom call to action block.'),
'render_template' => 'inc/blocks/accordion.php',
'category' => 'formatting',
'icon' => 'welcome-learn-more',
// icon is a dashicon https://developer.wordpress.org/resource/dashicons/
'keywords' => array( 'cta','call to action', 'plasterdog', 'custom' ),
'enqueue_style' => get_stylesheet_directory_uri() . '/inc/blocks/accordion.css',
'enqueue_script' => get_template_directory_uri() . '/inc/blocks/accordion.js',
));
}
}
RENDERING THE BLOCK
you’ll need to tell ACF how to render your block. It’s essentially the same process you’re used to for displaying custom fields – only that your HTML + PHP is wrapped in a function
(added to the functions file of your active theme)
function my_acf_block_render_callback( $block ) {
// convert name ("acf/accordion") into path friendly slug ("accordion")
$slug = str_replace('acf/', '', $block['name']);
// include a template part from within the "template-parts/block" folder for the block
if( file_exists( get_theme_file_path("/inc/blocks/accordion-{$slug}.php") ) ) {
include( get_theme_file_path("/inc/blocks/accordion-{$slug}.php") );
}
}
Once blocks have been registered and rendered ACF will see them

… and you can then create a field group and assign fields to it:

Breaking down the array that registers the block, first you name it
'name' => 'accordion',
give it a title
'title' => __('Accordion'),
then a description
'description' => __('A custom call to action block.'),
then provide the path to the php file which defines the block
'render_template' => 'inc/blocks/accordion.php',
which in this case reads like this
<!-- https://www.w3schools.com/howto/howto_js_accordion.asp -->
<button class="accordion" style="background-color:<?php the_field('block_faq_background_color'); ?>; color:<?php the_field('block_faq_text_color'); ?>;;">
<?php the_field('block_faq_accordion_teaser');?></button>
<div class="panel fade-in quarter">
<?php the_field('block_faq_accordion_content');?>
</div>
then define which category of block
'category' => 'formatting',
the relevant icon (see: https://developer.wordpress.org/resource/dashicons/)
'icon' => 'welcome-learn-more',
relevant keywords for your user to find them
‘keywords’ => array( ‘cta’,’call to action’, ‘plasterdog’, ‘custom’ ),
attaching a relevant stylesheet
'enqueue_style' => get_stylesheet_directory_uri() . '/inc/blocks/accordion.css',
which in this case reads
/*--- see: https://codepen.io/bluehaus/pen/EfGyi ---*/
@-webkit-keyframes fadeIn { from { opacity:0; } to { opacity:1; } }
@-moz-keyframes fadeIn { from { opacity:0; } to { opacity:1; } }
@keyframes fadeIn { from { opacity:0; } to { opacity:1; } }
.fade-in { opacity:0; /* make things invisible upon start */
-webkit-animation:fadeIn ease-in 1; /* call our keyframe named fadeIn, use animattion ease-in and repeat it only 1 time */
-moz-animation:fadeIn ease-in 1;
animation:fadeIn ease-in 1;
/* this makes sure that after animation is done we remain at the last keyframe value (opacity: 1)*/
-webkit-animation-fill-mode:forwards; -moz-animation-fill-mode:forwards; animation-fill-mode:forwards;
/*---nsets the default duration ---*/
-webkit-animation-duration:1s; -moz-animation-duration:1s; animation-duration:1s;}
.fade-in.quarter {-webkit-animation-delay: 0.1s; -moz-animation-delay: 0.1s; animation-delay: 0.1s; }
.fade-in.half {-webkit-animation-delay: 0.5s; -moz-animation-delay: 0.5s; animation-delay: 0.5s; }
.fade-in.full {-webkit-animation-delay: 1.25s; -moz-animation-delay: 1.25s; animation-delay: 1.25s;}
/* Style the buttons that are used to open and close the accordion panel */
.accordion {cursor: pointer;padding: 1em 2em;width: 100%;text-align: left;border: none;outline: none;transition: 0.4s;-webkit-border-radius: 10px;
border-radius: 10px;}
button.accordion {-webkit-box-shadow: 0 0 0 0 transparent;box-shadow: 0 0 0 0 transparent;text-shadow: 0 0 0 transparent;}
.accordion:hover{text-decoration: underline;}
.accordion:after {
font-family: "Font Awesome 5 Free"; font-weight: 900; content:"\f0d7"; font-size:1.5em;
float: right;
}
.accordion.active:after {
font-family: "Font Awesome 5 Free"; font-weight: 900; content:"\f0de"; font-size:1.5em;
float: right;
}
/* Add a background color to the button if it is clicked on (add the .active class with JS), and when you move the mouse over it (hover) */
.active, .accordion:hover {
background-color: #ccc;
}
/* Style the accordion panel. Note: hidden by default */
.panel {
padding: 1em 2em;
background-color: white;
display: none;
overflow: hidden;
transition: max-height 0.9s ease-out;
}
attach scripts if desired
'enqueue_script' => get_template_directory_uri() . '/inc/blocks/accordion.js',
which in this case is
/* SOURCE: https://www.w3schools.com/howto/howto_js_accordion.asp */
var acc = document.getElementsByClassName("accordion");
var i;
for (i = 0; i < acc.length; i++) {
acc[i].addEventListener("click", function() {
/* Toggle between adding and removing the "active" class,
to highlight the button that controls the panel */
this.classList.toggle("active");
/* Toggle between hiding and showing the active panel */
var panel = this.nextElementSibling;
if (panel.style.display === "block") {
panel.style.display = "none";
} else {
panel.style.display = "block";
}
});
}
and now you have a custom block interface

Some things to bear in mind:
- I couldn’t get any kind of conditional code to work inside of these blocks, and according to this link I am not alone
- You can get this all to work within a plugin, however you will still need to activate the ACF definitions either by importing a JSON version or including a PHP version in your theme
- There are already many blocks included with core and countless plugins which augment the options
- Block patterns are a better way to build a layout than with an elaborate custom block