Register an ACF block with modern practices

Is it possible to register an ACF block using block.json?

I’ve looked at this old post, but that still registers the block using acf_register_block_type

In my block.json:

"acf": {
		"mode": "preview",
		"renderTemplate": "/resources/views/blocks/dummy-box/dummy-box.php"


ACF fails to see my template however.

BTW, I used this handy utility to create the block setup:

After registering it with the correct path, it seems to have found it, I think.

add_action( 'init', 'register_acf_blocks' );
function register_acf_blocks() {
    register_block_type( __DIR__ . '/resources/views/blocks/dummy-box' );

Unfortunately, it doesn’t work with blade files; just strict php.

Would love to understand if there is a way to make this work with the Blade files :crossed_fingers:

It looks like ACF provide a render callback for block.json:

  • renderCallback (string) (optional) Instead of providing a render_template, a callback function name may be specified to output the block’s HTML.
// Specifying a function
"renderCallback": "my_acf_block_render_callback",

You could render a Blade view inside that callback using the view() function.

function your_blade_block_render_callback($block, $content, $is_preview, $post_id, $wp_block, $context) {
    echo view('blocks/example', compact('block', 'content', 'is_preview', 'post_id', 'wp_block', 'context'));

I haven’t tested this, but having a quick look at the ACF source shows that this is fired via call_user_func and return value discarded, so echoing rather than returning the output seems to be the way it’s supposed to work.

Slightly adjacent to this is this page from the Acorn docs on rendering Blade views.

here’s what I have been using:

 * Register ACF Blocks.
function theme_blocks_init()
    // Directory containing the blocks, within the 'resources/views' directory.
    $directory = resource_path('views') . '/blocks/';

    // Iterate over the directory provided and look for blocks.
    $block_directory = new DirectoryIterator($directory);

    foreach ($block_directory as $block) {
        if ($block->isDir() && !$block->isDot()) {

add_action('init', __NAMESPACE__ . '\\theme_blocks_init');

all your blocks will go in /resources/views/blocks/, each in their own directory

so for example, a carousel block would go in /resources/views/blocks/carousel/

each block has a block.json, along with a corresponding blade file - the file has to be named the same as the block slug, so in this example it would be ‘carousel.blade.php’

in the block.json:

  "acf": {
    "mode": "auto", // or whatever
    "renderCallback": "\\App\\blade_render_callback",
    "postTypes": ["page"], // or whatever
    "blockVersion": 2

then your callback function:

 * Callback for rendering Blade templates.
 * Name of the Blade template at resources/views/blocks/{title}.blade.php
function blade_render_callback($block, string $content = '', bool $is_preview = false, int $post_id = 0)
    $slug                = str_replace(THEME_BLOCK_SLUG . '/', '', $block['name']);
    $block['slug']       = $slug;

        echo \Roots\view('blocks.' . $block['slug'] . '.' . $block['slug'], [ 'block' => $block ])->render();

this is a truncated example, but it should work barebones, if you define the THEME_BLOCK_SLUG constant. That is going to be the block namespace. So in this example in the block.json the name is ‘jf/carousel’ - therefore the THEME_BLOCK_SLUG would be ‘jf’. You can hardcode it if you want; I just have a bunch of theme constants set earlier in the file that I use.