Field Forge supports registering Gutenberg blocks that render via PHP callback functions, with full access to custom fields inside the callback. The registration function is acf_register_block_type() (same as ACF, for zero-friction migration) or fieldforge_register_block_type() (native). Either way, you get the exact same workflow Advanced Custom Fields popularized — but on top of Field Forge’s custom table storage and AI features.
Gutenberg (WordPress’s block editor) was designed around JavaScript-rendered blocks. You write a save() function in JS that returns the block’s HTML, and WordPress saves that HTML in the post content. For simple visual blocks this works great.
But for dynamic blocks — “Show latest 3 blog posts with custom styling,” “Display a team member with data from the current user’s profile,” “Render a product grid from a WooCommerce query” — JavaScript blocks are painful. You end up writing REST API calls, managing loading states, and rebuilding data-fetching logic that WordPress already knows how to do in PHP.
PHP-rendered blocks let you write a PHP callback that runs at page render time, has full access to WordPress’s data APIs (WP_Query, get_field(), get_post_meta(), etc.), and returns the HTML directly. Simpler, faster, and more WordPress-idiomatic.
ACF introduced acf_register_block_type() years ago and it became the de facto standard for agency developers building custom Gutenberg blocks. Field Forge supports the same function signature so migration is trivial.
// functions.php or a custom plugin
add_action('acf/init', 'register_my_blocks');
function register_my_blocks() {
if (function_exists('acf_register_block_type')) {
acf_register_block_type([
'name' => 'feature-card',
'title' => __('Feature Card'),
'description' => __('A feature card with icon, title, and description.'),
'render_callback' => 'render_feature_card_block',
'category' => 'theme',
'icon' => 'star-filled',
'keywords' => ['feature', 'card'],
'supports' => [
'align' => ['wide', 'full'],
'anchor' => true,
'color' => true,
],
]);
}
}
function render_feature_card_block($block) {
$icon = get_field('icon');
$title = get_field('title');
$description = get_field('description');
?>
<div class="feature-card">
<div class="feature-card__icon"><?php echo esc_html($icon); ?></div>
<h3 class="feature-card__title"><?php echo esc_html($title); ?></h3>
<p class="feature-card__description"><?php echo esc_html($description); ?></p>
</div>
<?php
}
This code is identical to ACF’s registration code. Works unchanged after migration.
If you prefer Field Forge’s native API (for code cleanliness or to avoid implying ACF is still needed), use fieldforge_register_block_type():
add_action('init', function() {
fieldforge_register_block_type([
'name' => 'feature-card',
'title' => __('Feature Card'),
'render_callback' => 'render_feature_card_block',
'category' => 'theme',
]);
});
Both functions do the same thing. Use whichever fits your team’s conventions.
Each PHP block can have its own field group assigned via location rules:
Location Rules:
Block is equal to "acf/feature-card"
When an editor adds the block to a post, Field Forge renders the field group in the block’s sidebar (or inline, depending on block settings). The editor fills in the fields, the block auto-updates with live preview.
At render time, the render_callback function uses get_field() to access the field values:
function render_feature_card_block($block) {
// get_field() works inside block callbacks
$icon = get_field('icon');
$title = get_field('title');
// The $block array contains everything about this specific block instance
$block_id = $block['id'];
$is_preview = !empty($block['is_preview']);
$block_data = $block['data'];
$className = !empty($block['className']) ? $block['className'] : '';
// Render HTML
?>
<div class="feature-card <?php echo esc_attr($className); ?>">
<h3><?php echo esc_html($title); ?></h3>
</div>
<?php
}
PHP blocks support all standard Gutenberg block features:
'align' => ['wide', 'full']'anchor' => true for HTML anchor links'color' => ['text' => true, 'background' => true]All configured via the standard WordPress block support API.
When an editor is viewing the block in the editor (not on the frontend), the callback runs in “preview mode”:
function render_my_block($block) {
if ($block['is_preview']) {
// Rendering in the editor
echo '<div class="preview-wrapper">';
render_actual_block($block);
echo '</div>';
} else {
render_actual_block($block);
}
}
Use this to add editor-only wrappers, placeholder content when fields are empty, or special preview styling.
PHP blocks can contain other Gutenberg blocks via InnerBlocks:
acf_register_block_type([
'name' => 'two-column',
'render_callback' => 'render_two_column_block',
'supports' => [
'inner_blocks' => true,
],
]);
function render_two_column_block($block) {
?>
<div class="two-column">
<div class="column">
<InnerBlocks />
</div>
<div class="column">
<!-- Static content or another InnerBlocks -->
</div>
</div>
<?php
}
This lets you build reusable layout blocks that users can fill with any core Gutenberg blocks (paragraph, heading, image, etc.).
Pre-populate blocks with default inner content:
acf_register_block_type([
'name' => 'three-column',
'render_callback' => 'render_three_column_block',
'template' => [
['core/columns', [], [
['core/column', [], [['core/heading', ['content' => 'Column 1']]]],
['core/column', [], [['core/heading', ['content' => 'Column 2']]]],
['core/column', [], [['core/heading', ['content' => 'Column 3']]]],
]],
],
]);
Every acf_register_block_type() call in your existing theme works unchanged when Field Forge is active (via the compatibility layer). No code changes needed.
The migration process:
acf_register_block_type() calls continue to workMost sites with custom PHP blocks complete migration without touching block registration code.
PHP blocks render on the server, so they don’t add JavaScript bundle weight. Combined with Field Forge’s custom table storage, field value lookups inside render callbacks are fast. A page with 20 custom blocks runs in a fraction of the query count a similar JavaScript-rendered setup would need.
| PHP-rendered blocks | JavaScript Gutenberg blocks | |
|---|---|---|
| Language | PHP | JavaScript (React / JSX) |
| Build tooling | None | Webpack / Vite / wp-scripts |
| Dynamic data access | Easy (get_field(), WP_Query) |
Requires REST API calls |
| Developer experience | Familiar to PHP devs | Requires JavaScript skills |
| Client-side interactivity | Not possible | Full React component lifecycle |
| Server-side rendering | Yes (default) | Dynamic blocks only |
| Best for | Content blocks, dynamic queries, custom fields | Interactive widgets, live editing |
PHP blocks and JavaScript blocks aren’t mutually exclusive — use both on the same site for different purposes.
Get Field Forge — from $35/year →
PHP blocks support is included in every paid plan.