How Blocks Are Discovered

On the acf/init hook (priority 5), the function acf_blocks_load_blocks() reads block definitions from a generated manifest (includes/generated-block-manifest.php). The manifest is an OPcache-friendly PHP array of every block’s metadata and field groups, so production requests skip directory scans and JSON decoding entirely. When the manifest is missing, a filesystem scan of blocks/ takes over as a fallback. For each block, three things happen:
ACF block registration here is automatic: on acf/init at priority 5, the loader registers every block in the manifest (or, without a manifest, every folder with a valid block.json). No manual registration list to maintain. Blocks you disable through the Block Manager are skipped along with their field groups and editor assets.
- The block is registered with WordPress via
register_block_type(). - ACF field groups are loaded from JSON files in the same directory.
- An
extra.phpfile is included if it exists.
block.json Format
Every block uses ACF Block API v3 with standard WordPress block metadata. Here is a representative example:
{
"apiVersion": 3,
"name": "acf/accordion",
"title": "Accordion",
"description": "A customizable accordion block with FAQ schema support.",
"category": "acf-blocks",
"icon": "list-view",
"keywords": ["accordion", "faq", "toggle"],
"acf": {
"renderTemplate": "accordion-block.php",
"blockVersion": 3
},
"supports": {
"align": ["wide", "full"],
"mode": true,
"jsx": true,
"anchor": true
},
"style": "file:./accordion.css",
"editorStyle": "file:./accordion.css"
}Key fields:
name– Must start withacf/. This is the block’s unique identifier.category– Set toacf-blocksso the block appears in the ACF Blocks inserter category.acf.renderTemplate– The PHP file used to render the block. Path is relative to the block directory.acf.blockVersion– Set to3for ACF Block v3 behavior.supports.jsx– Whentrue, the block supports InnerBlocks.supports.mode– Whentrue, the block can switch between edit and preview mode.style– Path to the block’s CSS file, loaded on the frontend (conditional) and in the editor.editorStyle– Path to editor-only styles (often the same file asstyle).
Field Group Registration
The function acf_blocks_register_field_groups() scans each block directory for *.json files, skipping block.json. Each JSON file should contain an ACF field group definition with a key and fields array. The function calls acf_add_local_field_group() for each valid group.
Both single group objects and arrays of groups are supported:
{
"key": "group_accordion_fields",
"title": "Accordion",
"fields": [ ... ],
"location": [[
{ "param": "block", "operator": "==", "value": "acf/accordion" }
]],
"active": true
}The location rule ties the field group to its block. This is standard ACF configuration.
Because field groups are registered with acf_add_local_field_group(), they are “local” to the plugin. They do not appear in the ACF field group admin UI for editing and cannot be accidentally overwritten by database-stored groups.
Style Pre-Registration
On init (priority 5), acf_blocks_register_styles() scans all block directories and calls wp_register_style() for each block’s CSS file. The handle format is {block-name}-style (e.g. acf-accordion-style).
This pre-registration is what enables conditional loading. WordPress automatically enqueues a registered block style only on pages where that block is rendered, rather than loading all 29 stylesheets on every page.
The extra.php File
If a block directory contains an extra.php file, it is require_once‘d during block registration. This file typically registers AJAX handlers, content filters, or additional scripts.
Seven blocks include extra.php:
- star-rating-block – Registers the frontend rating script, the
acf-blocks/v1/ratingsREST route, and the legacy AJAX fallback for saving ratings. - post-display – Optimizes the ACF relationship field query for better performance.
- section-block – Registers an editor-only script with a custom
InnerBlockscomponent and the footer output for per-instance CSS. - toc-block – Adds heading ID attributes to post content.
- url-preview – Registers the admin AJAX handlers for fetching URL metadata and importing images.
- email-form – Registers the
email-form-proxy/v1/submitREST route that relays webhook submissions server-side (browsers block cross-origin webhook POSTs). - product-box – Registers the
product-box-image(550×550) andproduct-box-wide(800×450) image sizes.
Adding a New Block
To add a new block:
- Create a new directory under
blocks/(e.g.blocks/my-block/). - Add a
block.jsonwith the required metadata. - Add a
block-data.jsonwith the ACF field group definition. - Create the render template (e.g.
my-block.php). - Optionally add a CSS file and reference it in
block.json. - Optionally add an
extra.phpfor additional hooks.
One caveat since 2.9.0: when the generated manifest is present, the loader trusts it and won’t see a new folder. Delete includes/generated-block-manifest.php (the loader falls back to scanning blocks/) or regenerate the manifest from the repo’s build tooling, and the block is registered on the next page load. No changes to any other file are needed.
Quick answers to common questions:
How does block auto-discovery work?
acf_blocks_load_blocks() reads the generated block manifest and registers each entry with register_block_type(), loading the matching field groups. Without a manifest it falls back to globbing blocks/ for subdirectories with block.json, so a new folder with valid files still becomes a block on the next page load.
Can I disable specific blocks I don’t use?
Yes. The Performance & Block Manager card (Settings > ACF Blocks License) lets you disable blocks with checkboxes; disabled blocks skip registration, field groups, and editor assets. In code, use the acf_blocks_disabled_blocks filter. Unused blocks cost nothing on the frontend anyway; assets enqueue only when a block renders.