Switching ACF Custom Blocks to Block.json with ACF 6.0

ACF 6.0 has been released!

Look at that beautiful full width interface!

Included in this version we get a bunch of really nice UI upgrades for setting up fields. In addition, there are some big changes to how block registration works. ACF has switched to using the more WordPress-native method of registering with a block.json file.

Using block.json has several big benefits: you get more controls, a more straight forward syntax with script enqueueing built-in, and you can hook into @wordpress/scripts to compile assets for your blocks.

Let’s walk through how to convert a legacy ACF block to the new block.json format.

Legacy PHP Registration

Here’s an example legacy block that uses a few different options and fields:

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

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

        // Register a testimonial block.
			'name'            => 'basic-block',
			'title'           => 'Basic Block',
			'description'     => 'gutenberg-basic-block-acf-example',
			'render_callback' => 'acf_basic_block_callback',
			'category'        => 'formatting',
			'icon'            => 'admin-comments',
			'keywords'        => array('button', 'text', 'acf'),

Converting to Block.json

Before we convert this block, let’s take a look at all the options available to us with Block.json. The ACF documentation provides us with a good example:

    "name": "all-fields-block",
    "title": "All Fields Test Block",
    "description": "All fields test block",
    "style": "file:./all-fields-block.css",
    "category": "theme",
    "icon": "admin-comments",
    "apiVersion": 2,
    "keywords": [
    "acf": {
        "mode": "preview",
        "renderTemplate": "all-fields-block.php",
        "postTypes": [ "post" ],
    "styles": [
        { "name": "default", "label": "Default", "isDefault": true },
        { "name": "red", "label": "Red" },
        { "name": "green", "label": "Green" },
        { "name": "blue", "label": "Blue" }
    "supports": {
        "align": true,
        "anchor": true,
        "alignContent": false,
        "color": {
            "text": true,
            "background": true,
            "link": true
        "alignText": true,
        "spacing": {
            "margin": [
            "padding": true
        "typography": {
            "lineHeight": true,
            "fontSize": true
        "fullHeight": true
    "attributes": {
        "backgroundColor": {
            "type": "string",
            "default": "purple"
    "example": {
        "attributes": {
            "data": {
                "text": "This is an example text field",
                "text_area": "This is an example text area field"

There are a couple important keys in the json above to call out:

The “acf” key is unique to ACF blocks and allows us to define the mode, what post types this block is available for, and most importantly: the render template. This allows us to still write the frontend template for the block in regular PHP.

Additionally, take note of the “style” key. It points to a css file using a special relative url syntax prefixed with “file:”. This css will be specifically enqueued for this block.

Just as we can use this css property, we also now have access to an “editor script” property. In previous iterations, you may have enqueued this with the enqueue_block_editor_assets hook, but this new system is a more future-forward approach. You can actually specify scripts for just the backend, just the frontend, or both! You can find all the details for the editor script options in the codex.

Let’s look at that original PHP block registered with block.json instead:

  "name": "basic-block",
  "title": "Basic Block",
  "description": "gutenberg-basic-block-acf-example",
  "apiVersion": 2,
  "acf": {
    "renderTemplate": "basic-block-render.php"
  "category": "formatting",
  "icon": "admin-comments",
  "keywords": [

You can see most fields just transfer over, with a few key differences:

  1. There is no more render callback function. Instead you specify a render template php file under the “acf” key where you can output the markup for the block.
  2. There is an added “apiVersion” key. This is used to specify a version for how blocks behave. Version 2 is the default for blocks registered with block.json. This adds a wrapper so that innerBlocks are exactly the same on the frontend and in the editor. You can read more about this on the ACF website, but usually it is safe to assume version 2 is what you want.

Once you set up your block.json file and any scripts or css related to it, we still need a PHP one-liner to actually register the block. This can be added to the functions.php file, or better yet you can create an include file where all the blocks are registered:

function custom_load_blocks(){
  register_block_type( 'path/to/folder/containing/block.json' );
add_action( 'init', 'custom_load_blocks' );

The Future of ACF Blocks

This is a really exciting move to bring ACF blocks much closer to how native Gutenberg blocks function. This move will allow developers to setup themes where custom native blocks and ACF blocks can all reside in one shared “custom blocks” directory and we can use the @wordpress/scripts package to compile assets for everything at once.

If you want to jump right in, grab my theme starter designed for building blocks with ACF block.json.

This also will make the upgrade pipeline much smoother as new features become available within Gutenberg.

It’s a bit of a project to go through and convert all your blocks, but long term this upgrade will be super worth it.

Finally, I have lots of content planned around block theme development and custom ACF blocks, so consider signing up for my monthly newsletter if you are interested in learning more about advanced block development in WordPress.


  1. Florian Avatar

    What if we want to render with React? Can we still have access to avg fields ?

    1. Anton P. Avatar
      Anton P.


      I’m not sure I follow exactly. Are you rendering the front end or the editor view using React? or both? For the front end, what I’ve seen a lot of people do is output the ACF fields as a json encoded string into a hidden pre tag and then use react to grab the innerHTML contents and parse it into a JS object when you initialize your app. Feels hacky, but I see it done all over the place and seems to be a generally accepted solution.

Leave a Reply

Your email address will not be published. Required fields are marked *