Jump to Navigation Jump to Main Content Jump to Footer
Home » Docs » Features » Native and ACF block support with auto‑registration

Native and ACF block support with auto‑registration

Chisel auto-discovers and registers both native (Gutenberg) and ACF blocks at runtime, eliminating boilerplate. Drop your block code in src/blocks/ or src/blocks-acf/, run the build, and blocks appear in the editor—no manual registration needed.

The system handles asset enqueueing, Twig template discovery, block categories, and performance optimization automatically. Scripts and styles are registered from block.json file references, with optional critical CSS inlined for instant paint. Unused block styles are dequeued per page to keep your site fast.

Under the hood, RegisterBlocks factory scans compiled blocks in build/blocks/ and build/blocks-acf/, registers them via WordPress APIs, and wires Twig rendering through Timber. One convention, zero configuration, clean markup—out of the box.

Overview

Chisel provides automatic block discovery and registration for both native Gutenberg blocks and ACF blocks. The system compiles blocks from source folders to build directories, then discovers and registers them at runtime.

Key directories

PathPurpose
src/blocks/Native block source code
build/blocks/Compiled native blocks with block.json
src/blocks-acf/ACF block source code
build/blocks-acf/Compiled ACF blocks with block.json

Core classes

  • core/WP/Blocks.php — Native blocks lifecycle and hooks
  • core/WP/AcfBlocks.php — ACF blocks lifecycle and ACF JSON integration
  • core/Factories/RegisterBlocks.php — Block discovery and registration factory
  • core/Helpers/BlocksHelpers.php — Block rendering utilities and critical CSS handling

Auto-discovery workflow

Native blocks

File: core/WP/Blocks.php

Initialization on init hook:

  1. RegisterBlocks factory scans build/blocks/ directory
  2. Discovers all subdirectories containing block.json files
  3. Registers each block via register_block_type()
  4. Processes asset references (file:./...) in block.json
  5. Registers scripts/styles with proper handles and dependencies

Additional hooks:

  • block_categories_all → Adds custom block category at top of inserter (e.g., “Chisel Blocks”)
  • timber/locations → Extends Twig template locations to include src/blocks/{block-name}/
  • render_block → Adds custom BEM classes to block wrappers, removes layout classes
  • wp_print_styles → Dequeues unused block styles and inlines critical CSS

ACF blocks

File: core/WP/AcfBlocks.php

Initialization on acf/init hook:

  1. RegisterBlocks factory scans build/blocks-acf/ directory
  2. Discovers all ACF block block.json files
  3. Registers each ACF block via register_block_type()
  4. Processes assets same way as native blocks

ACF JSON integration:

  • acf/settings/load_json → Adds src/blocks-acf/{block}/acf-json as load path for each block
  • acf/settings/save_json → Automatically saves field groups to block’s acf-json folder
  • Keeps field definitions co-located with block code

Block registration factory

File: core/Factories/RegisterBlocks.php

The factory accepts 'wp' or 'acf' as block type and handles all registration logic.

Constructor responsibilities

  • Sets blocks folder based on type: blocks/ or blocks-acf/
  • Defines paths: src/{blocks-folder}/build/{blocks-folder}/
  • Scans for blocks via DirectoryIterator
  • Stores blocks list for registration

Registration process

Method: register_custom_blocks()

For each discovered block:

  1. Checks if block.json exists in build/{blocks-folder}/{block-name}/
  2. Decodes block.json to extract metadata
  3. Processes script/style fields: editorScripteditorStylestylescriptviewScriptviewStyle
  4. For each file:./... reference:
    • Converts to absolute URL
    • Loads .asset.php for dependencies and version
    • Registers style via wp_register_style()
    • Registers script via wp_register_script() or wp_register_script_module()
    • Respects ignoreScripts array (scripts build but don’t enqueue)
  5. Calls register_block_type() with path and modified metadata
  6. Includes optional init.php from block source folder if present

Script handle format

Handles follow pattern: block-{type}-{blockName}-{key}

Examples:

  • block-wp-accordion-style
  • block-acf-slider-viewScript

Block.json file structure

Script and style fields

All fields accept a string or array of file references using file:./... syntax.

FieldWhen loadedPurpose
editorScriptEditor onlyJS for block registration and edit component​
editorStyleEditor onlyCSS for block appearance in editor​
styleEditor and frontendShared CSS for both contexts​
scriptEditor and frontendShared JS for both contexts​
viewScriptFrontend onlyJS for frontend interactivity​
viewStyleFrontend onlyCSS for frontend-only styling​
viewScriptModuleFrontend onlyES module for frontend (registered via wp_register_script_module())​

Special properties

render — PHP callback for server-side rendering (not needed with Timber/Twig)

ignoreScripts — Array of script keys to build but not enqueue (useful when JS file only imports SCSS)

​Example block.json

{
  "apiVersion": 3,
  "name": "chisel/accordion",
  "title": "Accordion",
  "category": "chisel-blocks",
  "icon": "editor-justify",
  "editorScript": "file:./index.js",
  "editorStyle": "file:./index.css",
  "style": ["file:./style-index.css", "file:./style-script.css"],
  "script": "file:./script.js",
  "viewScript": "file:./view.js",
  "viewStyle": "file:./view.css",
  "ignoreScripts": ["script"]
}
JSON

Source file structure

Native block structure

Location: src/blocks/{block-name}/

Typical files:

  • index.js — Editor entry (imports edit.jsstyle.scss)
  • edit.js — Block edit component (imports editor.scss)
  • script.js — Shared entry (can import style.scsscritical.scss)
  • view.js — Frontend entry (imports view.scss)
  • style.scss — Shared styles
  • editor.scss — Editor-only styles
  • view.scss — Frontend-only styles
  • critical.scss — Critical/above-fold styles (optional)
  • render.twig or {block-name}.twig — Twig template
  • init.php — Optional PHP initialization logic

ACF block structure

Location: src/blocks-acf/{block-name}/

Similar to native blocks, plus:

  • {block-name}.twig — Twig template (matches block slug)
  • acf-json/ — ACF field group JSON files
  • Field groups assigned to block auto-save here

Build outputs

After running npm run build, webpack compiles blocks to build/blocks/ or build/blocks-acf/:

Generated files

From index.js:

  • index.js — Compiled editor script
  • index.asset.php — Dependencies and version
  • index.css — Compiled editor styles

From script.js:

  • script.js — Compiled shared script
  • script.asset.php — Dependencies and version
  • style-script.css — Compiled shared styles (from style.scss import)
  • script.css — Critical CSS (from critical.scss import)

From view.js:

  • view.js — Compiled frontend script
  • view.asset.php — Dependencies and version
  • view.css — Compiled frontend styles

Plus:

  • block.json — Copied/generated block metadata
  • {block-name}.twig — Copied Twig template

Twig template rendering

Native blocks

File: core/Helpers/BlocksHelpers.php

Method: render_twig_file()

Renders Twig templates from build/blocks/{block-name}/render.twig . Timber automatically discovers templates because Blocks::tiwg_files_locations() adds src/blocks/{block-name}/ to search paths .

ACF blocks

Method: acf_block_render()

Context variables provided:

  • block — Block data (name, ID, attributes, align, className)
  • post_id — Current post ID
  • slug — Block slug with b- prefix
  • is_preview — Whether rendering in editor
  • fields — ACF field values from get_fields()
  • block['class_names'] — Array of block classes
  • block['block_id'] — Block anchor or ID
  • wrapper_attributes — String of wrapper attributes for direct output

Filters available:

  • chisel_timber_acf_blocks_data — Global filter for all ACF blocks
  • chisel_timber_acf_blocks_data_{slug} — Block-specific filter
  • chisel_timber_acf_blocks_data_{block_id} — Instance-specific filter

Renders: build/blocks-acf/{block-slug}/{block-slug}.twig


Editor customizations

Custom block category

File: core/WP/Blocks.php

Method: block_categories()

Adds custom category at top of block inserter:

  • Slug: chisel-blocks
  • Title: {Theme Name} Blocks
  • Icon: Chisel logo SVG

Block pattern categories

Method: register_block_patterns_categories()

Default categories:

  • chisel-patterns/cta — Call to Action
  • chisel-patterns/features — Features Sections
  • chisel-patterns/hero — Hero Sections

Categories prefixed with theme name in labels .

Block alignment defaults

Method: blocks_alignment_data()

Sets default alignment for specific blocks via chisel_editor_scripts filter:

$editor_scripts_data['editor']['localize']['data']['blocksDefaultAlignment'] = array(
    'chisel/slider' => 'full',
);
PHP

Performance optimizations

Dequeue unused block styles

Files: core/WP/Blocks.phpcore/WP/AcfBlocks.php

Method: dequeue_blocks_styles()

On wp_print_styles (frontend only):

  1. Detects blocks used on current page via get_content_blocks_names()
  2. Dequeues style handles for blocks not present
  3. Reduces CSS payload significantly

Critical CSS inlining

Method: BlocksHelpers::get_block_inline_css()

For blocks present on page:

  1. Checks if {block}/script.css exists (built from critical.scss)
  2. Fetches CSS content via wp_remote_get()
  3. Inlines via wp_add_inline_style() to main stylesheet handle

Size limit: Configurable via chisel_styles_inline_size_limit filter (default: 10,000 bytes)

Separate core block assets

Filter: chisel_load_separate_core_block_assets

Controls whether WordPress loads core block styles separately or in single block-library.css (default: false for better performance) .

Fast refresh support

Condition: ThemeHelpers::is_fast_refresh()

In development mode, scripts in ignoreScripts are still registered to support live reload.


Block rendering modifications

File: core/WP/Blocks.php

Method: render_block()

Post-processes block HTML output:

  • Adds custom BEM classes via BlocksHelpers::get_block_object_classnames()
  • Format: c-block c-block--{namespace} c-block--{name}
  • Removes WordPress layout classes: is-layout-flowis-layout-constrained
  • Adds u-table-responsive to core/table blocks

Adding a new native block

  1. Create block folder: src/blocks/{block-name}/
  2. Add source files: index.jsedit.jsstyle.scsseditor.scss, etc.
  3. Create Twig template: {block-name}.twig or render.twig
  4. Run build: npm run build (or npm run dev)
  5. Block auto-registers on next page load

Optional: Add init.php for server-side logic


Adding a new ACF block

  1. Create block folder: src/blocks-acf/{block-name}/
  2. Add source files: Same as native blocks
  3. Create Twig template: {block-name}.twig (must match block slug)
  4. Run build: npm run build
  5. Create ACF field group in WordPress admin
  6. Assign to block: Set location rule to Block is equal to chisel/{block-name}
  7. Field group saves to src/blocks-acf/{block-name}/acf-json/ automatically
  8. Block auto-registers on acf/init

Critical CSS workflow

Setup

In script.js

import './style.scss';      // Builds to style-script.css
import './critical.scss';   // Builds to script.css (critical)
JavaScript

In block.json

{
  "style": ["file:./style-script.css"],
  "ignoreScripts": ["script"]
}
JSON

What happens

  • style-script.css — Enqueued normally (add to style array)
  • script.css — Fetched and inlined automatically when block is present
  • script.js — Not enqueued (in ignoreScripts) but still builds

Best practices

  • Keep critical.scss minimal (fonts, layout primitives, above-fold essentials)
  • Scope strictly to your block: .b-your-block { ... }
  • Don’t import critical.scss from editor-only entries
  • Monitor inline size (limit: 10KB by default)

Advanced customization

Custom script registration args

Filter: chisel_block_register_script_args

add_filter('chisel_block_register_script_args', function($args, $handle, $block) {
    $args['strategy'] = 'async'; // Change from default 'defer'
    return $args;
}, 10, 3);
PHP

Disable custom script registration

Filter: chisel_blocks_register_scripts

Set to false to let WordPress handle asset registration entirely from block.json without custom handles.

Block-specific init logic

Create init.php in block source folder:

File: src/blocks/{block-name}/init.php

// All variables from register_custom_blocks() available
// $block_name, $block_path, $block_metadata, etc.

add_filter('block_type_metadata', function($metadata) use ($block_name) {
    // Modify block metadata before registration
    return $metadata;
});
PHP

Troubleshooting

Block doesn’t appear

  • Verify build/blocks/{block}/block.json exists after build
  • Check file:./... references point to actual files
  • Confirm block category exists (chisel-blocks for custom blocks)
  • Review browser console for registration errors

Twig template not found

  • Ensure template is in src/blocks/{block}/ or src/blocks-acf/{block}/
  • Timber looks in source folders, not build folders
  • Template name should match block slug for ACF blocks

Styles not loading

  • Check if block is in ignoreScripts but not in style array
  • Verify webpack compiled SCSS to CSS in build folder
  • Confirm handle not dequeued by custom code

ACF field group not saving to block folder

  • Ensure location rule is Block is equal to chisel/{block-name}
  • Verify acf-json folder exists in src/blocks-acf/{block-name}/
  • Check folder permissions

Critical CSS not inlining

  • Confirm critical.scss imported in script.js
  • Verify script.css exists in build/{block}/ after build
  • Check inline size limit (default 10KB)
  • Ensure block is actually used on current page

Do you like Chisel?

Give it a star on GitHub!