2026年11月12日

2026年11月12日

ACF Flexible Content: Setting Up Layouts and Displaying Them

This article is a localized edition of the Japanese original. Read the Japanese original

The Flexible Content field in Advanced Custom Fields (ACF) lets you prepare layouts (building blocks) such as "heading", "text" and "image" in advance, so editors can arrange as many of them as they like, in any order, on each page. It suits landing pages and pages built by stacking sections.

This article explains how to create a Flexible Content field and the code that displays it in a theme template (as of October 2026; the display code was tested with Secure Custom Fields 6.9.7 and WordPress 6.9).

Requirements

  • Flexible Content is an ACF PRO feature. It is not included in the free version of ACF.
  • Secure Custom Fields, a plugin derived from ACF and distributed by WordPress.org, also has Flexible Content and is displayed with the same functions (have_rows(), get_row_layout() and so on).
  • The display code goes into the theme's PHP templates. Block themes (such as Twenty Twenty-Five) render pages with HTML templates, so the PHP code below assumes a page template in a classic theme (or its child theme).

Step 1: Create the Flexible Content field

  1. In the admin area, go to "ACF" → "Field Groups" and add a new field group.
  2. Add a field and choose "Flexible Content" as the field type.
  3. Enter a label (for example, Page sections) and a field name (for example, page_sections). The field name is used in the template code.
  4. In the field group's Location Rules, choose where it appears (for example, Post Type is equal to Page) and save.

The Flexible Content field has these main settings (from the official ACF documentation):

Setting Description
Layouts The layouts available on the edit screen
Button Label The text shown on the button that adds a layout
Minimum Layouts The minimum number of layouts required
Maximum Layouts The maximum number of layouts allowed

Step 2: Prepare layouts and sub fields

For each layout, set the Label, Name, Limit and Display, plus the fields used inside it (sub fields). This article uses the following three layouts as an example.

Layout name Sub field (name, type) Purpose
heading heading_text (Text) Section heading
text_block body (WYSIWYG Editor) Body text
image_block image (Image) Image
  • The layout "Name" is the value get_row_layout() returns in the template. Using letters, numbers and underscores makes it easier to handle in code.
  • For the image field, Return Format can be an image array, a URL or an ID. The code in this article uses Image ID. With an ID, wp_get_attachment_image() outputs an img tag with srcset.

After saving, open a page's edit screen. Add layouts with the field's button and confirm that you can reorder them.

Step 3: Display it in a template

Put the following code inside the loop of a page template (for example, page.php or a custom page template).

<?php
if ( have_rows( 'page_sections' ) ) :
	while ( have_rows( 'page_sections' ) ) :
		the_row();
		$layout = get_row_layout();

		if ( 'heading' === $layout ) {
			printf( '<h2>%s</h2>', esc_html( get_sub_field( 'heading_text' ) ) );

		} elseif ( 'text_block' === $layout ) {
			echo '<div class="section-text">' . wp_kses_post( get_sub_field( 'body' ) ) . '</div>';

		} elseif ( 'image_block' === $layout ) {
			$image_id = get_sub_field( 'image' ); // Field whose Return Format is set to "Image ID"
			if ( $image_id ) {
				echo wp_get_attachment_image( $image_id, 'large' );
			}
		}
	endwhile;
endif;
  • have_rows() returns true while layouts added to the page remain. the_row() moves to the next layout, get_row_layout() gets that layout's name, and get_sub_field() gets a sub field's value.
  • Escape values when you output them. Pass text through esc_html() and WYSIWYG values that contain HTML through wp_kses_post().
  • When you use it outside the template loop or display another page's values, pass the post ID, as in have_rows( 'page_sections', $post_id ).

Step 4: Split each layout into its own file

As layouts increase, the if branches get long. You can keep things organized by adding a file named after each layout to the theme and loading it with get_template_part().

<?php
while ( have_rows( 'page_sections' ) ) :
	the_row();
	get_template_part( 'template-parts/flexible/' . get_row_layout() );
endwhile;

In the theme's template-parts/flexible/heading.php, write only the display for that layout. Inside the loaded file, get_sub_field() still returns the current row's values.

<?php
// template-parts/flexible/heading.php
?>
<h2 class="section-heading"><?php echo esc_html( get_sub_field( 'heading_text' ) ); ?></h2>

Create text_block.php and image_block.php the same way. Layouts without a matching file display nothing.

Step 5: Check the output

On a page's edit screen, add a few layouts, fill in the content, save, and view the page.

  • Do the sections appear in the order you added them?
  • If nothing appears, check that the field name in the template (page_sections) and the layout names match the ACF settings exactly
  • If nothing appears where the image should be, or Array is shown, check that the image field's Return Format is "Image ID"

Notes for ongoing use

  • Do not rename layouts later: Pages already saved keep their data under the old name. After a rename, those rows no longer match the template's branches and stop displaying.
  • Manage field group settings as JSON: ACF's Local JSON (a feature that saves field group settings as files when you create an acf-json folder in your theme) makes it easier to keep settings in sync between production and development.
  • Limit the number of layouts per page: Use Minimum Layouts, Maximum Layouts and each layout's Limit to keep choices manageable for editors.

Summary

  • Flexible Content is a feature of ACF PRO (or Secure Custom Fields) that lets editors arrange prepared layouts on each page
  • Decide the layout names and sub field names, and set the image Return Format to "Image ID"
  • In templates, get values with have_rows(), the_row(), get_row_layout() and get_sub_field(), and escape output with esc_html() and wp_kses_post()
  • When layouts increase, split them into per-layout files with get_template_part()

Related articles

Questions about WordPress? The publisher of this site is based in Japan and replies in Japanese. Contact (Japanese)

お気軽にご相談ください

お見積りへ お問い合わせへ
▲