2026年7月23日

2026年7月23日

ACFのフレックスコンテンツでページのセクションを自由に組み替える実装方法

ACFの「フレックスコンテンツ」フィールドを使うと、見出しブロック・本文・画像と文章の横並び・CTAボタンといった「セクションの型」をあらかじめ用意し、編集者が好きな順番で積み上げてページを作れるようになります。この記事では、フィールドの設計からテーマ側の出力コード、運用でつまずきやすい点までを順に説明します。読み終えるころには、固定ページのレイアウトをコードに触れずに組み替えられる仕組みを自分のテーマに組み込めるはずです。

フレックスコンテンツはACF PROの機能

最初に確認しておきたいのが、フレックスコンテンツは無料版ACFには含まれず、ACF PRO の機能だという点です。リピーター、ギャラリー、クローン、オプションページも同じくPRO側の機能です。管理画面でフィールドタイプを選ぶ一覧にフレックスコンテンツが出てこない場合は、無料版を使っている可能性が高いと考えてください。

また、2024年にWordPress.org側でACFのフォークである「Secure Custom Fields」が配布されるようになり、ACFの入手経路が分かれました。サイトに入っているのがACF PRO(開発元から入手したもの)なのか、別の配布物なのかで使える機能や更新の流れが異なるため、プラグイン一覧で名称と作者を確かめてから作業に入ると混乱しません。機能構成は変わることがあるため、最新の状況は各配布元の説明で確認してください(2026年10月現在)。

作業は本番サイトではなく、ステージング環境やローカル環境で行い、事前にデータベースとテーマファイルのバックアップを取っておきます。テーマを直接編集するとテーマ更新で上書きされるため、子テーマか自作テーマで実装するのが前提です。

フィールドグループを設計する

レイアウトは「セクションの型」として考える

フレックスコンテンツの中には複数の「レイアウト」を定義します。レイアウトひとつが、ページ上の1セクションの型に当たります。たとえば次のような構成にすると、多くのサービス紹介ページや採用ページを組めます。

  • hero:大見出し・リード文・背景画像
  • text_block:WYSIWYGエディタの本文
  • media_text:画像と文章の横並び(左右の向きを選ぶボタングループ付き)
  • cta:見出しとリンクボタン

各レイアウトには「ラベル」と「名前」があり、テンプレートで分岐に使うのは名前の方です。名前は英小文字とアンダースコアで付け、公開後は変更しないのが原則です。レイアウト名を変えると、すでに保存されている行と定義が対応しなくなり、既存ページの表示が崩れることがあります。

サブフィールドの戻り値を決めておく

画像フィールドは戻り値を「画像ID」にしておくと、テンプレート側で wp_get_attachment_image() を使ってsrcsetやalt付きのimgタグを出力でき、レスポンシブ画像の扱いが楽になります。リンクフィールドは戻り値を配列にすると、URL・テキスト・ターゲットをまとめて受け取れます。

レイアウトごとの最小数・最大数、ボタンの文言、編集画面での表示形式(ブロック・表・行)も設定できます。画面上の項目名はACFのバージョンや表示言語で変わるため、目的に合うものを探して設定してください。

フィールドグループをJSONで管理する

テーマ内に acf-json フォルダを作っておくと、フィールドグループを保存するたびにJSONファイルとして書き出され、Gitで差分を管理できます。ステージングで作った定義を本番へ持っていくときも、管理画面での手作業を減らせます。

テンプレートで出力する

基本形:have_rows と get_row_layout

フレックスコンテンツの出力は、have_rows() で行をループし、the_row() で現在の行に進め、get_row_layout() でレイアウト名を取得して分岐する、という流れです。サブフィールドは get_sub_field() で取り出します。

<?php
// page.php などのループ内。フィールド名は page_sections とする
if ( have_rows( 'page_sections' ) ) :
	while ( have_rows( 'page_sections' ) ) :
		the_row();
		$layout = get_row_layout();

		if ( 'hero' === $layout ) :
			$heading  = get_sub_field( 'heading' );
			$image_id = get_sub_field( 'image' ); // 戻り値: 画像ID
			?>
			<section class="sec sec--hero">
				<?php
				if ( $image_id ) {
					echo wp_get_attachment_image( $image_id, 'large' );
				}
				?>
				<h2><?php echo esc_html( $heading ); ?></h2>
			</section>
			<?php
		elseif ( 'text_block' === $layout ) :
			?>
			<section class="sec sec--text">
				<?php echo wp_kses_post( get_sub_field( 'body' ) ); ?>
			</section>
			<?php
		endif;
	endwhile;
endif;

出力時のエスケープは省略しないでください。プレーンテキストは esc_html()、属性値は esc_attr()、URLは esc_url()、HTMLを含むWYSIWYGの本文は wp_kses_post() を通します。編集者が信頼できる人だけでも、アカウントの乗っ取りや貼り付けたコードの混入に備えて、出力側で安全にしておくのが基本です。近年のACFでは the_field() 系の出力にもHTMLの安全化処理が加わっていますが、自分で get_sub_field() の値を出力する場合はテンプレート側の責任と考えておきます。

レイアウトごとにテンプレートパーツへ分ける

レイアウトが増えると、1ファイルの if 分岐はすぐに読みにくくなります。レイアウト名とファイル名を揃えてテンプレートパーツに分けると、追加や修正がしやすくなります。

<?php
if ( have_rows( 'page_sections' ) ) {
	while ( have_rows( 'page_sections' ) ) {
		the_row();
		// レイアウト名をファイル名に使うため、念のため英小文字・数字・_・- だけにする
		$layout = sanitize_key( get_row_layout() );
		get_template_part( 'template-parts/sections/' . $layout );
	}
}

読み込まれたテンプレートパーツの中でも、行ループの中にいる状態は保たれるため、そのまま get_sub_field() が使えます。たとえば template-parts/sections/cta.php は次のように書けます。

<?php
$heading = get_sub_field( 'heading' );
$link    = get_sub_field( 'button' ); // リンクフィールド(戻り値: 配列)
?>
<section class="sec sec--cta">
	<?php if ( $heading ) : ?>
		<h2><?php echo esc_html( $heading ); ?></h2>
	<?php endif; ?>
	<?php if ( $link ) : ?>
		<?php $target = $link['target'] ? $link['target'] : '_self'; ?>
		<a class="btn" href="<?php echo esc_url( $link['url'] ); ?>" target="<?php echo esc_attr( $target ); ?>">
			<?php echo esc_html( $link['title'] ); ?>
		</a>
	<?php endif; ?>
</section>

get_template_part() は該当ファイルが見つからなくてもエラーを出さず、何も表示しません。新しいレイアウトを追加したのに画面に出ないときは、レイアウト名とファイル名が一致しているかを最初に確認してください。

レイアウトの中にリピーターを入れる

料金表やFAQのように、セクションの中に繰り返し要素がある場合は、レイアウトのサブフィールドにリピーターを置きます。出力はループを入れ子にするだけで、内側でも have_rows() と the_row() を使います。内側のループを抜けると、外側の行の位置に戻ります。

運用でつまずきやすい点

表示されないときの確認順

何も表示されないときは、次の順で確認すると原因を絞り込みやすくなります。

  1. フィールドグループの表示条件(ロケーションルール)が、そのページや投稿タイプに合っているか
  2. テンプレートに書いたフィールド名とレイアウト名が、管理画面の「名前」と一字一句同じか(ラベルではない)
  3. そのページが実際にそのテンプレートファイルで表示されているか
  4. ページキャッシュが古い表示を返していないか

フィールド名が合っているかを確かめるには、一時的に get_field( 'page_sections' ) の結果をステージング環境だけで確認する方法が手早いです。戻り値は行の配列で、各行に acf_fc_layout というキーでレイアウト名が入っています。

行数とデータ量

フレックスコンテンツは、サブフィールドの値を1つずつ投稿メタとして保存します。セクション数とサブフィールド数が多いページでは投稿メタの件数が増え、編集画面の表示や保存が重くなることがあります。レイアウトに最大数を設定する、使わないサブフィールドを作らない、といった設計段階の工夫が効きます。

ブロックエディタとの使い分け

最近のWordPressでは、ブロックエディタ(パターンやACF Blocksを含む)でもセクション単位のページ作りができます。フレックスコンテンツは「デザインの型を厳密に固定し、編集者には中身だけを入れてもらいたい」場合に向いています。自由度を広く与えたい場合はブロックの方が合うこともあるため、誰がどこまで編集するのかを先に決めてから選ぶと、後からの作り直しを避けられます。

まとめ

  • フレックスコンテンツはACF PROの機能。入っているACFの種類と提供元を最初に確認する
  • レイアウトは「セクションの型」として設計し、テンプレートの分岐に使うレイアウト名は公開後に変えない
  • 出力は have_rows() → the_row() → get_row_layout() で分岐し、get_sub_field() の値は出力前に esc_html() などでエスケープする
  • レイアウトごとにテンプレートパーツへ分け、ファイル名とレイアウト名を揃えると保守しやすい
  • 作業は子テーマとステージング環境で行い、フィールド定義は acf-json で管理する

お気軽にご相談ください

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