Flexible Content in ACF (SCF) lets you stack blocks of different shapes in any order to assemble a page. Prepare parts like “heading plus text,” “two images side by side” and “contact button,” and you can build a landing page just by rearranging things in the editor.
On the template side there’s really one key idea—use get_row_layout() to check what kind of row you’re on and output accordingly.
How it differs from a repeater field
- Repeater field—adds more rows of the same shape (FAQs, a staff list)
- Flexible content—stacks blocks of different shapes in any order (landing pages, service pages)
A structure like this, for example, can be rearranged freely from the editor.
Page body (flexible content: blocks)
├── hero (heading, tagline, background image)
├── text (a single rich text field)
├── two_columns (image plus text, two columns)
├── text (another block of text)
└── cta (button label and link target)TipsHow to output an ACF repeater fieldIf all you need is more rows of the same shape, a repeater field is the better fitThe basic pattern: branch on get_row_layout
You enter the same have_rows() loop as a repeater, then check the type of each row and output different HTML.
<?php if (have_rows('blocks')) : ?>
<?php while (have_rows('blocks')) : the_row(); ?>
<?php if (get_row_layout() === 'hero') : ?>
<section class="hero">
<?php echo wp_get_attachment_image(get_sub_field('image'), 'full'); ?>
<h1><?php the_sub_field('title'); ?></h1>
</section>
<?php elseif (get_row_layout() === 'text') : ?>
<section class="text">
<?php the_sub_field('body'); ?>
</section>
<?php elseif (get_row_layout() === 'cta') : ?>
<section class="cta">
<a href="<?php echo esc_url(get_sub_field('url')); ?>"><?php the_sub_field('label'); ?></a>
</section>
<?php endif; ?>
<?php endwhile; ?>
<?php endif; ?>What get_row_layout() returns is the “name” (slug) you gave the layout in the field settings. It’s not the label shown in the admin screen (“Hero” and so on) but the alphanumeric name in the field below it, so check the settings screen first.
Once you have a lot of branches, switch reads better.
<?php switch (get_row_layout()) :
case 'hero': ?>
<!-- HTML for the hero -->
<?php break;
case 'cta': ?>
<!-- HTML for the CTA -->
<?php break;
endswitch; ?>Split the template up to keep it tidy
The weak point of flexible content is that left alone, the template gets long. Split files by layout name and the main file comes down to a few lines.
<?php while (have_rows('blocks')) : the_row(); ?>
<?php get_template_part('parts/flex/' . get_row_layout()); ?>
<?php endwhile; ?>Lay them out inside your theme like this.
your-theme/
└── parts/
└── flex/
├── hero.php
├── text.php
├── two-columns.php
└── cta.phpInside the part that gets loaded, get_sub_field() still works as usual (the context of the current row is preserved).
<section class="cta">
<a class="button" href="<?php echo esc_url(get_sub_field('url')); ?>">
<?php the_sub_field('label'); ?>
</a>
</section>Using a repeater inside a block
You can put a repeater field inside a flexible content block. Inside, it’s the usual three-part set.
<div class="cards">
<?php if (have_rows('items')) : ?>
<?php while (have_rows('items')) : the_row(); ?>
<div class="card">
<?php echo wp_get_attachment_image(get_sub_field('image'), 'medium'); ?>
<h3><?php the_sub_field('title'); ?></h3>
</div>
<?php endwhile; ?>
<?php endif; ?>
</div>When should you use this instead of the block editor?
Today’s WordPress has the block editor, so if all you want is “stack things freely,” the editor can do that too. Flexible content shines when you want people to use only the blocks you’ve decided on, so the design can’t break.
- Flexible content—nothing beyond the blocks you prepared can be created, so the design stays consistent. Good for delivering company sites and landing pages
- Block editor—more freedom, which also means updates can break the design. Good for blog posts
“Give the person doing the updates a screen where they can’t get lost”—that’s the real value of flexible content.
Summary
- Flexible content stacks blocks of different shapes in any order. If the rows are all the same shape, a repeater is enough
- The loop is the same as a repeater. Branch on
get_row_layout()to output different HTML - When the branches pile up, split files with
get_template_part('parts/flex/' . get_row_layout()) - The layout name is the alphanumeric name in the settings. Branch on the display label and nothing appears
Switch to thinking in terms of “prepare the parts people can stack,” and you can build pages that hold up long after they’re delivered.