Last updated Mar 23, 2023


Included in ACF PRO is a powerful PHP-based framework for developing custom block types.

ACF blocks are highly customisable and powerfully dynamic. They integrate deeply with custom fields allowing PHP developers to create bespoke solutions inline with WordPress theme development.

A graphic showing an ACF Block with an inset code editor showing block.json content.

Simplified example of registering a custom testimonial block.


🌎 PHP Environment

ACF blocks is a PHP framework and does not require any JavaScript. This differentiates itself from the WordPress block API which relies heavily on modern JavaScript techniques, syntax and build tools.

🎨 Simple Templating

Similar to WP theme development, ACF blocks are rendered using a PHP template file or callback function allowing full control over the output HTML.

🔌 Custom Fields Compatible

ACF blocks offer full compatibility with all field types including both the Repeater and Clone fields!

It’s a similar story for template functions too. Whether you are loading a field value via get_field(), or looping over a Repeater field using have_rows(), the experience remains familiar and consistent to regular theme development.

👀 Live Previews

Content changes, and so do block previews! When editing an ACF block, the HTML will update in the backend giving you a real time preview of your content. If you use an ACF block in a Query Loop block or in a Full Site Editing Block Theme, your block preview will automatically update as your query changes.

🌈 Native Compatibility

Believe it or not, ACF blocks maintain native compatibility with WordPress core. This allows features such as “alignment”, “anchor” and “re-usable blocks” to work!

🎉 Anywhere and everywhere

ACF blocks are not tied to metadata, meaning they can be used anywhere in Gutenberg, and multiple times per post.


ACF Blocks is a premium feature found in ACF PRO version 5.8.0 and above. If not already, please consider upgrading to take advantage of this premium feature! ACF PRO version 6.0.0 introduced even more features and a new modernized registration system that matches current WordPress practice, bringing ACF blocks even closer to native WordPress blocks. For this new method of block registration, you need WordPress 5.8 (or WordPress 6.0, if you want to store your ACF Blocks inside a theme)

Key Concepts

Before reading any further, it’s a good idea to familiarize yourself with some of the concepts introduced by the Gutenberg editor.

  • Blocks are an abstract unit for organizing and composing content introduced in WordPress 5.0. If it helps, you can think of blocks as a more graceful shortcode, with rich formatting tools for users to compose content.
  • Blocks can be static or dynamic. ACF Blocks are dynamic, meaning they are rendered server-side and allow for PHP logic.
  • ACF Blocks are registered and customized within the functions.php file using PHP and do not require any knowledge of React or the WP blocks JavaScript API.
  • ACF blocks differ from WP blocks in that the data is decoupled from the design. This allows for faster development of blocks by focusing only on the HTML output.
  • Block data is saved within the “post_content” as an HTML comment. This makes them unique to metaboxes which save data to the “postmeta” table.

Getting Started

The ACF Blocks framework performs a lot of “magic” behind the scenes to offer an intuitive development experience. We’ve simplified the process down to just three steps:

1. Register a Block

In ACF PRO 6.0, block.json became the recommended way of registering blocks – matching the native WordPress registration methods. This requires WordPress 5.8+. If you’re on an earlier version of ACF or WordPress, you can continue to use the previous function: acf_register_block_type()

Block.json blocks store block metadata inside a “block.json” file inside your plugin, or since WordPress 6.0, your theme. This makes blocks much more portable, and easier to share between projects.

💡 This example only uses a small handful of the available settings so please be sure to read the block.json ACF configuration doc for a full list, along with WordPress’s block.json documentation.

Here’s an example block.json configuration object, we’re storing this in blocks/testimonial/block.json in our theme or plugin:


    "name": "acf/testimonial",
    "title": "Testimonial",
    "description": "A custom testimonial block.",
    "style": [ "file:./testimonial.css" ],
    "category": "formatting",
    "icon": "admin-comments",
    "keywords": ["testimonial", "quote"],
    "acf": {
        "mode": "preview",
        "renderTemplate": "testimonial.php"
    "align": "full"

Aside from the ACF key, everything else in a block.json file is native WordPress configuration.

Once you’ve got your block.json file, you need to add some PHP to register it with the WordPress function register_block_type. You can either put this in your plugin’s PHP file, or in your theme’s functions.php file or equivalent:

add_action( 'init', 'register_acf_blocks' );
function register_acf_blocks() {
    register_block_type( __DIR__ . '/blocks/testimonial' );

2. Create a Field Group

The next step is to create a field group for your block. Note that any and all ACF fields can be used within your block – there are no limitations!

That said, we don’t recommend using complex or large amounts of fields. Keep your blocks as lightweight and simple as possible.

From the location rules, use the “Block” rule to select your newly registered block type.

Testimonial Field Group Edit Screen Screenshot

Screenshot of field group settings and block location rule.

3. Render the Block

Lastly, you’ll need to tell ACF how to render the block, which is essentially the same process you’re used to for displaying custom fields.

This is done by creating a template file within your theme that matches the render_template setting used when registering the block. In this example, the template file will be called ‘blocks/testimonial/testimonial.php’.

💡 There are multiple ways to render a block. Please read the ACF block.json configuration docs for a full description on the render_template and render_callback configuration keys inside the ACF object..

One very exciting feature of ACF Blocks is that all the ACF API function such as get_field(), the_field() and have_rows() will work as expected!


 * Testimonial Block Template.
 * @param   array $block The block settings and attributes.
 * @param   string $content The block inner HTML (empty).
 * @param   bool $is_preview True during backend preview render.
 * @param   int $post_id The post ID the block is rendering content against.
 *          This is either the post ID currently being displayed inside a query loop,
 *          or the post ID of the post hosting this block.
 * @param   array $context The context provided to the block by the post or it's parent block.

// Support custom "anchor" values.
$anchor = '';
if ( ! empty( $block['anchor'] ) ) {
    $anchor = 'id="' . esc_attr( $block['anchor'] ) . '" ';

// Create class attribute allowing for custom "className" and "align" values.
$class_name = 'testimonial-block';
if ( ! empty( $block['className'] ) ) {
    $class_name .= ' ' . $block['className'];
if ( ! empty( $block['align'] ) ) {
    $class_name .= ' align' . $block['align'];

// Load values and assign defaults.
$text             = get_field( 'testimonial' ) ?: 'Your testimonial here...';
$author           = get_field( 'author' ) ?: 'Author name';
$author_role      = get_field( 'role' ) ?: 'Author role';
$image            = get_field( 'image' ) ?: 295;
$background_color = get_field( 'background_color' );
$text_color       = get_field( 'text_color' );

// Build a valid style attribute for background and text colors.
$styles = array( 'background-color: ' . $background_color, 'color: ' . $text_color );
$style  = implode( '; ', $styles );

<div <?php echo $anchor; ?>class="<?php echo esc_attr( $class_name ); ?>" style="<?php echo esc_attr( $style ); ?>">
    <blockquote class="testimonial-blockquote">
        <span class="testimonial-text"><?php echo esc_html( $text ); ?></span>
        <span class="testimonial-author"><?php echo esc_html( $author ); ?></span>
        <span class="testimonial-role"><?php echo esc_html( $author_role ); ?></span>
    <div class="testimonial-image">
        <?php echo wp_get_attachment_image( $image, 'full' ); ?>

You’ll note that our block.json includes a stylesheet. The format of this in our example is called a WPDefinedAsset. You can also pass a style handle which has already been registered by wp_register_style() – but you must have registered this style before your block registration. For further details on registering scripts and styles for blocks, please check our What’s new with ACF Blocks in ACF 6 documentation.


That’s all there is to it! You can immediately start using your new block within Gutenberg and place it anywhere within your content 💯.


Are blocks a replacement for metaboxes?
No. Metaboxes are still an important part of the content editing and theme development process. In fact, ACF will continue to utilize them as the primary tool for saving content.
Can I make changes to the field group?
Yes. You can make changes to your field group and fields at any time. Making changes like this will not cause any damage to already existing blocks.
Can I make changes to the template?
Yes. You can make changes to your block template or callback function at any time. ACF blocks are 100% dynamic meaning that they are rendered by the server each time they are loaded. This allows changes to block templates to be applied all existing block content.
Can I register a block without fields?
Yes. There are many scenarios where a block does not require any fields, such as a “latest post” block.
Is ACF Blocks included in the free version?
No. Contrary to our original post back in 2018, ACF blocks are only included in our professional version. This decision to go “Pro only” came after realising the amount of time and attention this feature will require over the coming years.
Do I need to write any JavaScript?
No. The ACF blocks framework is 100% PHP. If your block requires some JS for added functionality (a carousel slider for example), you can add this also.
Where is block data saved?
WordPress saves block data as HTML comments in the post_content. ACF blocks follow suit and save their data as a JSON object within that HTML comment.
Can I load values from other blocks?
Yes and No. Unlike loading a value from a post or user, block values are saved in the block HTML comment found within the post_content. This prevents the $post_id parameter from working as expected in our template functions. If you really need to, you can load the post_content of a given post, and then parse the blocks using the parse_blocks() function but we do not recommend this for performance reasons.
Do ACF blocks support native components?
Not yet. We are currently experimenting in this area and hope to roll out support for native block components in the future.
What is Gutenberg and what are blocks?
Introduced in WordPress 5.0, the block-based editor “Gutenberg” has transformed the way content is created. Content is now created in the unit of blocks instead of freeform text. Blocks take various forms including Paragraphs, Headings, Media and Embeds.