Tips・WordPress custom fields (ACF / SCF)

Building pages with ACF flexible content

How to stack blocks in any order and assemble a landing page with ACF (SCF) flexible content. Covers switching on get_row_layout and splitting your template into files to keep it readable, with code.

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 fit

The 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.php

Inside 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

  1. Flexible content stacks blocks of different shapes in any order. If the rows are all the same shape, a repeater is enough
  2. The loop is the same as a repeater. Branch on get_row_layout() to output different HTML
  3. When the branches pile up, split files with get_template_part('parts/flex/' . get_row_layout())
  4. 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.

FAQ

What's the difference between flexible content and a repeater field?
A repeater adds more "rows of the same shape," while flexible content lets you stack "blocks of different shapes" in any order. Use flexible content when you want to freely arrange things like a heading-plus-text block, a two-image block and a CTA block.
How do I change the output for each layout?
Call get_row_layout() inside the loop and it returns that row's layout name (its slug). Branch on it with switch or if, and output the HTML for each layout.
Can I use flexible content in the free version of ACF?
With ACF (the WP Engine version) you need the paid ACF PRO. SCF, distributed by WordPress officially, includes it for free, so with SCF you can use it right away.
My template is getting way too long.
Move each layout's HTML into its own file (for example parts/flex/hero.php) and load it with get_template_part('parts/flex/' . get_row_layout()); that collapses everything into one line.