ACF Flexible Content Fields create drag-and-drop page builders through multiple layout types within a single field allowing editors complete layout control. From text blocks and image galleries to testimonial carousels and call-to-action sections, flexible content eliminates template constraints. This comprehensive guide teaches flexible content setup, layout configuration, get_row_layout() conditional display, nested flexible fields, programmatic registration, and advanced techniques building custom WordPress page builders with ACF PRO.
What Is Flexible Content?
Flexible Content Concept:
Flexible Content (ACF PRO) allows users to choose from multiple layout types and arrange them in any order:
- Text Block: Heading, paragraph, alignment
- Image Block: Image, caption, size
- Video Block: Video URL, thumbnail
- Gallery: Multiple images
- Testimonial: Quote, author, photo
- Call to Action: Heading, text, button
Key Benefits:
- Drag-and-drop reordering
- Unlimited layout combinations
- Different fields per layout type
- Complete editor flexibility
- No template changes needed
Creating Flexible Content Field
Add Flexible Content via ACF UI:
- Custom Fields → Add Field
- Field Type: Flexible Content (ACF PRO)
- Configuration:
- Field Label: Page Content
- Field Name: page_content
- Button Label: “Add Content Block”
- Min/Max Layouts: Set limits (optional)
Add Layout Types:
Click “Add Layout” for each content type:
Layout 1: Text Block
- Layout Name: text_block
- Layout Label: Text Block
- Sub-fields:
- heading (Text)
- content (Wysiwyg Editor)
- alignment (Select: left/center/right)
Layout 2: Image Block
- Layout Name: image_block
- Layout Label: Image Block
- Sub-fields:
- image (Image)
- caption (Text)
- size (Select: small/medium/large/full)
Layout 3: Gallery
- Layout Name: gallery
- Layout Label: Gallery
- Sub-fields:
- images (Gallery)
- columns (Number)
Basic Flexible Content Loop
Display Layouts:
<?php
if (have_rows('page_content')) :
while (have_rows('page_content')) : the_row();
if (get_row_layout() == 'text_block') :
// Text Block Layout
$heading = get_sub_field('heading');
$content = get_sub_field('content');
$alignment = get_sub_field('alignment');
?>
<section class="content-block text-block text-<?php echo esc_attr($alignment); ?>">
<?php if ($heading) : ?>
<h2><?php echo esc_html($heading); ?></h2>
<?php endif; ?>
<?php if ($content) : ?>
<div class="content">
<?php echo wp_kses_post($content); ?>
</div>
<?php endif; ?>
</section>
<?php elseif (get_row_layout() == 'image_block') :
// Image Block Layout
$image = get_sub_field('image');
$caption = get_sub_field('caption');
$size = get_sub_field('size');
?>
<section class="content-block image-block size-<?php echo esc_attr($size); ?>">
<?php if ($image) : ?>
<figure>
<img src="<?php echo esc_url($image['sizes'][$size]); ?>"
alt="<?php echo esc_attr($image['alt']); ?>" />
<?php if ($caption) : ?>
<figcaption><?php echo esc_html($caption); ?></figcaption>
<?php endif; ?>
</figure>
<?php endif; ?>
</section>
<?php elseif (get_row_layout() == 'gallery') :
// Gallery Layout
$images = get_sub_field('images');
$columns = get_sub_field('columns');
?>
<section class="content-block gallery-block columns-<?php echo esc_attr($columns); ?>">
<?php if ($images) : ?>
<div class="gallery-grid">
<?php foreach ($images as $image) : ?>
<div class="gallery-item">
<img src="<?php echo esc_url($image['sizes']['medium']); ?>"
alt="<?php echo esc_attr($image['alt']); ?>" />
</div>
<?php endforeach; ?>
</div>
<?php endif; ?>
</section>
<?php endif;
endwhile;
endif;
?>Complete Page Builder Example
Advanced Layout Types:
<?php
if (have_rows('page_content')) :
while (have_rows('page_content')) : the_row();
// Text Block
if (get_row_layout() == 'text_block') :
get_template_part('template-parts/blocks/text-block');
// Hero Section
elseif (get_row_layout() == 'hero') :
$title = get_sub_field('hero_title');
$subtitle = get_sub_field('hero_subtitle');
$background = get_sub_field('background_image');
$button_text = get_sub_field('button_text');
$button_link = get_sub_field('button_link');
?>
<section class="content-block hero-block"
style="background-image: url(<?php echo esc_url($background['url']); ?>);">
<div class="hero-content">
<?php if ($title) : ?>
<h1><?php echo esc_html($title); ?></h1>
<?php endif; ?>
<?php if ($subtitle) : ?>
<p class="subtitle"><?php echo esc_html($subtitle); ?></p>
<?php endif; ?>
<?php if ($button_text && $button_link) : ?>
<a href="<?php echo esc_url($button_link); ?>" class="btn btn-primary">
<?php echo esc_html($button_text); ?>
</a>
<?php endif; ?>
</div>
</section>
// Two Column Layout
elseif (get_row_layout() == 'two_columns') :
$left_content = get_sub_field('left_column');
$right_content = get_sub_field('right_column');
?>
<section class="content-block two-columns">
<div class="row">
<div class="col-left">
<?php echo wp_kses_post($left_content); ?>
</div>
<div class="col-right">
<?php echo wp_kses_post($right_content); ?>
</div>
</div>
</section>
// Testimonials
elseif (get_row_layout() == 'testimonials') :
if (have_rows('testimonial_items')) :
?>
<section class="content-block testimonials-block">
<div class="testimonials-grid">
<?php while (have_rows('testimonial_items')) : the_row();
$quote = get_sub_field('quote');
$author = get_sub_field('author');
$company = get_sub_field('company');
$photo = get_sub_field('photo');
?>
<div class="testimonial">
<blockquote>
<?php echo esc_html($quote); ?>
</blockquote>
<div class="testimonial-author">
<?php if ($photo) : ?>
<img src="<?php echo esc_url($photo['sizes']['thumbnail']); ?>"
alt="<?php echo esc_attr($author); ?>" />
<?php endif; ?>
<div class="author-info">
<strong><?php echo esc_html($author); ?></strong>
<?php if ($company) : ?>
<span><?php echo esc_html($company); ?></span>
<?php endif; ?>
</div>
</div>
</div>
<?php endwhile; ?>
</div>
</section>
<?php endif;
// Call to Action
elseif (get_row_layout() == 'cta') :
$heading = get_sub_field('cta_heading');
$text = get_sub_field('cta_text');
$button = get_sub_field('cta_button');
$background_color = get_sub_field('background_color');
?>
<section class="content-block cta-block"
style="background-color: <?php echo esc_attr($background_color); ?>;">
<div class="cta-content">
<?php if ($heading) : ?>
<h2><?php echo esc_html($heading); ?></h2>
<?php endif; ?>
<?php if ($text) : ?>
<p><?php echo esc_html($text); ?></p>
<?php endif; ?>
<?php if ($button) : ?>
<a href="<?php echo esc_url($button['url']); ?>" class="btn btn-large">
<?php echo esc_html($button['title']); ?>
</a>
<?php endif; ?>
</div>
</section>
<?php endif;
endwhile;
endif;
?>Using Template Parts
Organize Layouts in Separate Files:
main-template.php:
<?php
if (have_rows('page_content')) :
while (have_rows('page_content')) : the_row();
$layout = get_row_layout();
// Load template part based on layout name
get_template_part('template-parts/blocks/' . $layout);
endwhile;
endif;
?>template-parts/blocks/text_block.php:
<?php
$heading = get_sub_field('heading');
$content = get_sub_field('content');
$alignment = get_sub_field('alignment');
?>
<section class="content-block text-block text-<?php echo esc_attr($alignment); ?>">
<?php if ($heading) : ?>
<h2><?php echo esc_html($heading); ?></h2>
<?php endif; ?>
<?php if ($content) : ?>
<div class="content">
<?php echo wp_kses_post($content); ?>
</div>
<?php endif; ?>
</section>Programmatic Flexible Content Registration
Register via PHP:
function mytheme_register_flexible_content() {
if (function_exists('acf_add_local_field_group')) {
acf_add_local_field_group(array(
'key' => 'group_page_builder',
'title' => 'Page Builder',
'fields' => array(
array(
'key' => 'field_page_content',
'label' => 'Page Content',
'name' => 'page_content',
'type' => 'flexible_content',
'button_label' => 'Add Content Block',
'layouts' => array(
'text_block' => array(
'key' => 'layout_text_block',
'name' => 'text_block',
'label' => 'Text Block',
'display' => 'block',
'sub_fields' => array(
array(
'key' => 'field_heading',
'label' => 'Heading',
'name' => 'heading',
'type' => 'text',
),
array(
'key' => 'field_content',
'label' => 'Content',
'name' => 'content',
'type' => 'wysiwyg',
),
),
),
'image_block' => array(
'key' => 'layout_image_block',
'name' => 'image_block',
'label' => 'Image Block',
'display' => 'block',
'sub_fields' => array(
array(
'key' => 'field_image',
'label' => 'Image',
'name' => 'image',
'type' => 'image',
'return_format' => 'array',
),
array(
'key' => 'field_caption',
'label' => 'Caption',
'name' => 'caption',
'type' => 'text',
),
),
),
),
),
),
'location' => array(
array(
array(
'param' => 'post_type',
'operator' => '==',
'value' => 'page',
),
),
),
));
}
}
add_action('acf/init', 'mytheme_register_flexible_content');Conditional Layout Display
Show Layouts Based on Conditions:
<?php
$layout_index = 0;
if (have_rows('page_content')) :
while (have_rows('page_content')) : the_row();
$layout_index++;
$layout = get_row_layout();
// Add special class to first layout
$first_class = ($layout_index === 1) ? 'first-block' : '';
// Add container to specific layouts only
$needs_container = in_array($layout, array('text_block', 'cta'));
?>
<section class="content-block <?php echo esc_attr($layout); ?> <?php echo esc_attr($first_class); ?>">
<?php if ($needs_container) : ?>
<div class="container">
<?php endif; ?>
<?php
// Display layout content
get_template_part('template-parts/blocks/' . $layout);
?>
<?php if ($needs_container) : ?>
</div>
<?php endif; ?>
</section>
<?php endwhile;
endif;
?>Nested Flexible Content
Flexible Content Within Flexible Content:
<?php
if (have_rows('page_content')) :
while (have_rows('page_content')) : the_row();
if (get_row_layout() == 'section_with_blocks') :
$section_title = get_sub_field('section_title');
?>
<section class="multi-block-section">
<h2><?php echo esc_html($section_title); ?></h2>
<?php
// Nested flexible content
if (have_rows('section_blocks')) :
while (have_rows('section_blocks')) : the_row();
if (get_row_layout() == 'feature') :
$feature_icon = get_sub_field('icon');
$feature_title = get_sub_field('title');
$feature_text = get_sub_field('text');
?>
<div class="feature">
<?php if ($feature_icon) : ?>
<img src="<?php echo esc_url($feature_icon['url']); ?>" alt="" />
<?php endif; ?>
<h3><?php echo esc_html($feature_title); ?></h3>
<p><?php echo esc_html($feature_text); ?></p>
</div>
<?php endif;
endwhile;
endif;
?>
</section>
<?php endif;
endwhile;
endif;
?>Dynamic Classes and Styling
Add Custom Classes from Fields:
<?php
if (have_rows('page_content')) :
while (have_rows('page_content')) : the_row();
$layout = get_row_layout();
$custom_class = get_sub_field('custom_css_class');
$background_color = get_sub_field('background_color');
$padding = get_sub_field('padding_size');
$classes = array(
'content-block',
$layout,
$custom_class,
'padding-' . $padding,
);
$style = $background_color ? 'background-color: ' . esc_attr($background_color) : '';
?>
<section class="<?php echo esc_attr(implode(' ', array_filter($classes))); ?>"
<?php if ($style) : ?>style="<?php echo $style; ?>"<?php endif; ?>>
<?php get_template_part('template-parts/blocks/' . $layout); ?>
</section>
<?php endwhile;
endif;
?>Layout Clone Field
Reuse Layout Configurations:
// Create base layout configuration
acf_add_local_field_group(array(
'key' => 'group_block_settings',
'title' => 'Block Settings',
'fields' => array(
array(
'key' => 'field_block_settings',
'label' => 'Block Settings',
'name' => 'block_settings',
'type' => 'group',
'sub_fields' => array(
array(
'key' => 'field_margin',
'label' => 'Margin',
'name' => 'margin',
'type' => 'select',
'choices' => array(
'none' => 'None',
'small' => 'Small',
'medium' => 'Medium',
'large' => 'Large',
),
),
array(
'key' => 'field_animation',
'label' => 'Animation',
'name' => 'animation',
'type' => 'select',
'choices' => array(
'none' => 'None',
'fade-in' => 'Fade In',
'slide-up' => 'Slide Up',
),
),
),
),
),
));
// Clone in flexible content layouts
array(
'key' => 'field_settings_clone',
'label' => 'Settings',
'name' => 'settings',
'type' => 'clone',
'clone' => array('field_block_settings'),
)Best Practices
Organize Layout Files:
theme/
├── template-parts/
│ └── blocks/
│ ├── text_block.php
│ ├── image_block.php
│ ├── gallery.php
│ ├── hero.php
│ ├── testimonials.php
│ └── cta.php
Use Consistent Naming:
- Layout names: snake_case (text_block, image_block)
- Layout labels: Title Case (Text Block, Image Block)
- CSS classes: kebab-case (text-block, image-block)
Escape Output:
<?php echo esc_html($text); ?>
<?php echo esc_url($url); ?>
<?php echo esc_attr($class); ?>
<?php echo wp_kses_post($html_content); ?>Conclusion
ACF Flexible Content Fields create modular page builders through multiple layout types, get_row_layout() conditional display, and drag-and-drop reordering eliminating rigid template structures. Register layouts programmatically with acf_add_local_field_group(), organize layout templates in separate files with get_template_part(), implement nested flexible content for complex page structures, and add custom classes and styling options for design flexibility. Flexible content empowers editors to build custom page layouts without developer intervention maintaining consistent design patterns.
External Links
- ACF Flexible Content Documentation
- get_row_layout() Function
- Page Builder Patterns
- Clone Field Type
- Layout Display Settings
Call to Action
Page builder configurations need reliable backups. Backup Copilot Pro protects your WordPress flexible content layouts and custom field data automatically. Safeguard your modular page structures—start your free 30-day trial today!
