{ Building Modular & Future-Proof WordPress Websites with ACF Blocks }
Modern WordPress development has evolved far beyond page templates and shortcodes. Today, professional developers treat WordPress as a component-driven system, where every section — hero banners, pricing tables, FAQs, testimonials — is a self-contained, reusable module.
In this guide, we’ll walk through how to build modular, scalable, and maintainable WordPress websites using ACF Blocks with JSON registration — the same approach I use in production for real-world client websites.
Why Move to Modular ACF Blocks
Traditional WordPress themes often rely on messy PHP templates and scattered get_field() calls. This approach leads to:
- Hard-to-maintain codebases
- Repeated logic across templates
- Poor reusability between projects
With Modular ACF Blocks, you can:
- Treat each section as a self-contained “block”
- Let editors use them visually in Gutenberg
- Keep PHP, HTML, CSS, and JS neatly scoped
- Reuse the same blocks across multiple projects
Think of it like React components — but inside WordPress.
Step 1: Create the Block Folder Structure
Organize your theme like this:
1/template-parts/
2 └── blocks/
3 └── hero/
4 ├── hero.php
5 ├── hero.scss
6 └── block.json
7 └── preview.pngEach block should include:
- A PHP template (hero.php)
- Optional SCSS/JS files
- A block.json file that defines the block metadata
- A preview.png image for block preview when we browse blocks on the Gutenberg’s “Add Block” panel. To preview the block design while choosing.
Step 2: Register the Block
Using PHP (Classic Way)
You can register your block using acf_register_block_type() inside your theme’s functions.php (or in a custom plugin):
1<?php
2add_action('acf/init', 'register_hero_block');
3function register_hero_block() {
4 if (function_exists('acf_register_block_type')) {
5 acf_register_block_type([
6 'name' => 'hero',
7 'title' => __('Hero Section'),
8 'description' => __('A full-width hero banner with image and overlay.'),
9 'render_template' => 'template-parts/blocks/hero/hero.php',
10 'category' => 'layout',
11 'icon' => 'cover-image',
12 'keywords' => ['hero', 'banner'],
13 'supports' => [
14 'align' => ['full', 'wide'],
15 'jsx' => true,
16 ],
17 ]);
18 }
19}This makes the block appear in Gutenberg’s “Add Block” panel.
Using JSON Registration (Modern Way)
From WordPress 5.8 onward, you can register blocks automatically using a block.json file.
This makes your blocks portable and compatible with Full Site Editing.
block.json
1{
2 "name": "acf/hero",
3 "title": "Hero Block",
4 "description": "A full-width hero section with image, overlay, and text.",
5 "category": "theme-blocks",
6 "icon": "cover-image",
7 "keywords": ["hero", "banner"],
8 "acf": {
9 "mode": "preview",
10 "renderTemplate": "hero.php"
11 },
12 "supports": {
13 "align": ["full", "wide"],
14 "anchor": true
15 },
16 "example": {
17 "attributes": {
18 "mode": "preview",
19 "data": {
20 "preview_image": "preview.png"
21 }
22 }
23 }
24
25}
26Then register it in your functions.php:
1<?php
2// ACF Blocks registration
3add_action('init', function() {
4 register_block_type(get_template_directory() . '/template-parts/blocks/hero');
5});
6
7// To enable the block preview thumbnail
8function theme_get_block_preview_image($block)
9{
10 $html = '';
11
12 if (is_admin() and isset($_POST['action']) and isset($_POST['block'])) {
13 $block_data_str = stripslashes($_POST['block']);
14 $block_data_obj = json_decode($block_data_str);
15
16 if (is_object($block_data_obj) and is_object($block_data_obj->data) and isset($block_data_obj->data->preview_image)) {
17 if (file_exists($block['path'] . '/' . $block_data_obj->data->preview_image)) {
18 $home_url = home_url();
19 $path = str_replace(ABSPATH, '', $block['path']);
20 $path = "/{$path}/";
21 $preview_url = $home_url . $path . $block_data_obj->data->preview_image;
22
23 $html = '<img width="470" src="' . esc_url($preview_url) . '">';
24 }
25 }
26 }
27
28 return $html;
29}
30No more acf_register_block_type() clutter — WordPress reads it directly from the JSON file.
Step 3: Create ACF Fields for the Block
In your WordPress dashboard:
- Go to ACF Custom Fields → Add New.
- Name it “Hero Section Fields.”
- Add fields like:
- hero_title (Text)
- hero_subtitle (Text)
- hero_image (Image)
- hero_overlay_color (Color Picker)
- Under Location, set:
Block → is equal to → Hero Block
Now ACF automatically binds these fields to your block inside the editor.
Step 4: Render the Fields in PHP
Create your hero.php file:
1<?php
2/**
3 * Hero Block Template
4 */
5
6// Preview the block design while browsing blocks
7$img = theme_get_block_preview_image($block);
8if ($img) {
9 echo $img;
10 return;
11}
12
13// Support custom "anchor" values.
14$anchor = '';
15if (!empty($block['anchor'])) {
16 $anchor = 'id="' . esc_attr($block['anchor']) . '" ';
17}
18// Create class attribute allowing for custom "className" and "align" values.
19$class_name = 'block-' . str_replace(['/', '_'], '-', $block['name']);
20if (!empty($block['className'])) {
21 $class_name .= ' ' . $block['className'];
22}
23// Full Site Editing for layouts
24if (!empty($block['align'])) {
25 $class_name .= ' align' . $block['align'];
26}
27
28// Generate unique ID for the block
29$block_id = 'id="' . 'block-acf-' . uniqid() . '" ';
30$anchor = $anchor ? $anchor : $block_id;
31
32// Get the ACF fields
33$hero_title = get_field('hero_title');
34$hero_subtitle = get_field('hero_subtitle');
35$hero_image = get_field('hero_image');
36$overlay_color = get_field('hero_overlay_color') ?: '#000000';
37?>
38
39<!-- Render the block HTML -->
40<section <?php echo $anchor ?> class="<?php echo $class_name ?> hero-block" style="background-image:url('<?php echo esc_url($hero_image['url']); ?>')">
41 <div class="hero-overlay" style="background-color:<?php echo esc_attr($overlay_color); ?>"></div>
42 <div class="hero-content">
43 <h1><?php echo esc_html($hero_title); ?></h1>
44 <p><?php echo esc_html($hero_subtitle); ?></p>
45 </div>
46</section>
47Step 5: Style Your Block
hero.scss
1// Hero block styles
2.hero-block {
3 position: relative;
4 background-size: cover;
5 background-position: center;
6 color: #fff;
7 padding: 6rem 2rem;
8 text-align: center;
9
10 .hero-overlay {
11 position: absolute;
12 inset: 0;
13 opacity: 0.5;
14 }
15
16 .hero-content {
17 position: relative;
18 z-index: 2;
19 max-width: 800px;
20 margin: 0 auto;
21 }
22}
23Compile your SCSS or enqueue the compiled CSS via wp_enqueue_style().
Step 6: Make It Reusable Across Projects
Once your block works, you can copy the entire /hero folder to another project, register it via JSON, and it’s ready to go — instantly.
For agency-style reuse:
- Store all blocks in a /blocks folder
- Version control them with Git
- Maintain a personal block library for future sites
Final Block Preview
Gutenberg “Add Block” panel preview

Frontend Preview

Bonus: Add Dynamic Logic
Make your blocks smarter with PHP conditions — for example, auto-detecting the homepage:
1<?php
2if (is_front_page()) {
3 echo '<p class="badge">Homepage Hero</p>';
4}Or dynamically adjust overlay brightness, fetch related posts, or pull dynamic colors.
Developer Takeaway
Benefit
What It Means
Reusable
Copy blocks across projects instantly
Maintainable
Self-contained PHP, JSON, and styles
Future-proof
Fully compatible with Gutenberg & Full Site Editing (FSE)
Client-friendly
Drag-and-drop visual editing
✍️ Closing Thoughts
ACF Blocks let you build enterprise-grade WordPress websites with the flexibility of a page builder and the precision of custom code.
Instead of writing repetitive templates, you now design reusable building blocks — each with its own logic, style, and presentation.
Whether it’s a startup website or a corporate platform, this approach gives you:
- Cleaner code
- Faster development
- Happier clients
Start small — build one ACF Block today. Soon, you’ll have a personal library of powerful, production-ready WordPress components.
🏷️ Keywords
ACF Blocks, WordPress Development, Gutenberg, JSON Registration, Modular Websites, PHP, Custom Fields, Full-Site Editing, Component-Driven Development