Roots Discourse

Sage, Gutenberg and ACF Blocks


Hi there,

After reading this post about the release of the first Gutenberg-compatible ACF version (beta), I played with it and tried to reduce the work to create a custom block to the minimum.

I wrote an article about it here (sorry it’s in french):

The idea is to be able to create a bloc like we’re used to create a custom template: just create a file with the right name in the right folder, and with the right meta datas as PHP comments.

I got it working by adding this in my theme:

Then I was able to create a new bloc in views/blocks like this:

It’s not battle tested as I just discovered it yesterday, but it looks like there’s no limitation in use by doing that. I’m sure the code can be improved though!

What do you think?



I mostly just wanted to post here because no one else has. What you are doing is awesome, for I see this work flow taking over in my agency as we move from using flexible ACF fields to ACF Blocks. Keep up the good work, in the coming months we will be trying this out and working this or a similar process into production. Thank you for a great starting point.



This is really cool. I’m going to try my hand at this for the site I’m currently building out.

It’s tough going building blocks without React knowledge – involves a lot of poking around in DevTools. Meanwhile people are yelling at me to get stuff done. Lifesaver really :pray:t4:



Working more and more with modern frontend framework, I find it harder and harder to work on WordPress themes because we still lack a component-based approach. Blade helps to decompose views, but I think the next step would be to have something like Vue single file components. Sage tooling could really help in this way. IMO, what’s missing to get a full component-based approach would be:

  • ACF fields definition embedded in component file itself: need to hook the way ACF looks for JSON file I guess
  • Javascript per-component ; Sage per-route approach is useful but limited. Sure we can insert a script tag in our block template but really not optimal
  • A way to chunk CSS & JS in the file: like Vue allows to use pre/post-processors and import other files. We’d need a specific Webpack loader for this, but it’s really something I’m not good at :slight_smile:

Of course we could use React or anything else and use WP API, but I think this way makes it lose a lot of what make WordPress useful: SEO friendly by default, lot of boilerplate (like head tags), variety of plugins, and so on ; So I don’t believe in this solution except for some very specific projects.

PS. Just sharing some thoughts, nothing in the works atm!



Scripts can be scoped & managed like this:

<section data-{{$block['id']}}>

  (function($) {
    var $block = $('[data-{{$block['id']}}]')

Of course it’s pretty simple and misses a lot of features offered by Sage and all of its Webpack tooling. That’s a first step though!

1 Like


Thank you for sharing this @nicooprat. While I tried to use it, it didn’t work for me until I added a few class backslashes to the script (new \DirectoryIterator, and \locate_template):

Lines 4 and 10



Another small bit to change:

	'render_callback' => function( $block ) {
	    $slug             = str_replace( 'acf/', '', $block['name'] );
	    $block['slug']    = $slug;
	    $block['classes'] = implode( ' ', [ $block['slug'], $block['className'], $block['align'] ] );
	    echo \App\template( "blocks/${slug}", [ 'block' => $block ] );

Since it wasn’t picking up callback function. Probably an issue with how PHP 7.3 calls namespaced functions inside setup.php.

1 Like


I’ve gotten custom blocks to work, and even render properly in the preview window during page editing, but I can’t seem to find out how to use blade in the template files. It never processes the blade syntax, and I’m forced to use regular ol’ PHP for my custom blocks, which is a real bummer. Any idea where I might have gone wrong?



Nevermind! I finally see where I went wrong.

echo \App\template("blocks/${slug}", ['block' => $block]);

In fact, if anyone is having a hard time getting the style to show up properly in the preview window, just load your main.css file like so:

function my_acf_admin_head() {
    <style type="text/css">

        <?php echo file_get_contents(get_theme_file_path() . "/dist/styles/main.css"); ?>


add_action('acf/input/admin_head', 'my_acf_admin_head');

I didn’t come up with that, just spreadin the word. Credit to



@nicooprat would you mind if I packaged your code up into a composer package for Sage? I’d like to be able to install it quickly and easily on projects. We could also probably feature it on the Roots site as a suggested add-on.

I’ll give you full credit for the code.



No problem of course, that’s a great idea! Glad it’s useful :wink:



Fantastic. I’ve published an early version here: I’ll write something more substantial about it after I test it more thoroughly. Thank you!



This works perfect! Thank you very much!

The only issue i’m having is that any of the public functions for my variables declared with in my App controller aren’t getting recognized.

Is there something I need to do so that the new blocks directory can recognize them?



This is much more @withjacoby’s area than mine, but my understanding is that Controller doesn’t yet work within the scope of Gutenberg blocks.




    function sage_blocks_callback($block)
  // Set up the slug to be useful
    $slug = str_replace('acf/', '', $block['name']);
    // Set up the block data
    $block['slug'] = $slug;
    $block['classes'] = implode(' ', [$block['slug'], $block['className'], 'align'.$block['align']]);
    // Use Sage's template() function to echo the block and populate it with data
    echo \App\template("blocks/${slug}", ['block' => $block]);


function sage_blocks_callback($block)
  // Set up the slug to be useful
    $slug = str_replace('acf/', '', $block['name']);
    // Set up the block data
    $block['slug'] = $slug;
    $block['classes'] = implode(' ', [$block['slug'], $block['className'], 'align'.$block['align']]);
    // Use Sage's template() function to echo the block and populate it with data
$data = collect(get_body_class())->reduce(function ($data, $class) use ($template) {
			return apply_filters("sage/template/{$class}/data", $data, $template);
    	}, []);
		$data['block'] = $block;
    echo \App\template("blocks/${slug}", $data);

should work.

1 Like


Awesome. I’ll add this to the library as soon as I can!

1 Like


Turns out this doesn’t work because $template isn’t defined. I’m sure it’s solvable and I’ll look into as I have time. Just wanted to get back to you!



Have been working on a different approach which we use in our office (still inspired on work by @nicooprat), which I wanted to share:

Block registration is entirely different, but it provides “Controller” functionality for your ACF Blocks.
Supports plain PHP templates and Blade. You can also create your own Block constructor (maybe you want to use Twig + Controller?).



I’m a bit surprised that this thread doesn’t get more attention. Not much love for Gutenberg I guess, even with very promising things ACF is doing :slight_smile:

@nicooprat thanks for starting this discussion and sharing your work. it’s working beautifully!

@codepuncher looks like you figured out “controller” functionality so now this workflow has everything to start creating blocks. Though I’m having trouble setting it up and get this error:

Fatal error: Uncaught Error: Class 'App\Testimonial' not found in /srv/www/ on line  *39*

Where do I register blocks with this code?

add_filter('acf_gutenblocks/blocks', function (array $blocks): array {
    $new_blocks = [
    return array_merge($blocks, $new_blocks);

I placed it in setup.php.

Also, is there a difference where I put Blocks/ folder? Currently, it’s inside the app.




The block directory location doesn’t matter as the code will run it from wherever the class is located.

Have you got the full class use statement in setup.php? I should update the docs.

Try this:

use App\Blocks\Testimonial\Testimonial;

add_filter('acf_gutenblocks/blocks', function (array $blocks): array {
    $new_blocks = [
    return array_merge($blocks, $new_blocks);

Note how I added Testimonial twice.
Hope that works.