TechEarl

How to Get the Current ACF Flexible Content Layout Name

get_row_layout() returns the current layout's name inside an ACF Flexible Content loop. Plus the canonical dispatch pattern using template parts, the switch-based pattern, and what to do when get_row_layout returns nothing.

Ishan Karunaratne⏱️ 4 min readUpdated
Share thisCopied
get_row_layout returns the active layout name inside a Flexible Content loop. Dispatch with template parts, switch pattern, and why it returns nothing.

get_row_layout() returns the name of the current Flexible Content layout inside a have_rows() loop. The name is whatever you registered as the layout's "Name" in the field group editor (snake_case by convention). The two canonical patterns for dispatching off that name are template parts (cleanest at scale) and switch statements (fine for small handfuls of layouts). If get_row_layout returns nothing, you forgot the_row() or you are outside the loop entirely.

Jump to:

The basic call

Inside a have_rows() loop, after the_row(), get_row_layout() returns the active layout's name:

php
if ( have_rows( 'page_builder' ) ) {
    while ( have_rows( 'page_builder' ) ) {
        the_row();
        $layout = get_row_layout();
        // $layout === 'hero' or 'cta' or 'stat_row' etc.
    }
}

The name returned matches what you typed into the "Name" field in the layout's field group editor. Match-cased; use snake_case as your convention.

Dispatch pattern 1: template parts

This is the canonical agency pattern. Each layout has a matching PHP partial under template-parts/flexible-content/{layout-name}.php. The loop just maps layout name to partial:

php
if ( have_rows( 'page_builder' ) ) {
    while ( have_rows( 'page_builder' ) ) {
        the_row();
        $layout = get_row_layout();
        get_template_part( 'template-parts/flexible-content/' . $layout );
    }
}

The partial reads its own sub-fields via get_sub_field():

php
<?php
// template-parts/flexible-content/hero.php
$heading = get_sub_field( 'heading' );
$subheading = get_sub_field( 'subheading' );
?>
<section class="hero">
    <h1><?php echo esc_html( $heading ); ?></h1>
    <p class="lead"><?php echo esc_html( $subheading ); ?></p>
</section>

Why template parts win at scale: adding a new layout means creating one PHP file. Removing one means deleting one file. No central switch statement to edit, no merge conflicts when two devs add layouts simultaneously. This is the pattern every agency project I have shipped in the last decade uses, covered in the broader context of Why Many Agencies Still Prefer ACF Over Gutenberg.

Dispatch pattern 2: switch statement

For small handfuls of layouts (three or four), inline dispatch is fine:

php
if ( have_rows( 'page_builder' ) ) {
    while ( have_rows( 'page_builder' ) ) {
        the_row();
        switch ( get_row_layout() ) {
            case 'hero':
                $heading = get_sub_field( 'heading' );
                printf( '<h1>%s</h1>', esc_html( $heading ) );
                break;
            case 'cta':
                $text = get_sub_field( 'text' );
                $url = get_sub_field( 'url' );
                printf( '<a href="%s">%s</a>', esc_url( $url ), esc_html( $text ) );
                break;
            default:
                // unknown layout
        }
    }
}

The default case matters: if you change a layout name in the field group but forget to update the template, the default branch catches it silently. Log unknown layouts in dev so you notice:

php
default:
    if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
        error_log( 'Unknown flex layout: ' . get_row_layout() );
    }

Mapping layout names to renderer callbacks

For projects where the layouts are programmatically defined (rare on agency work, common on more bespoke setups), an associative-array dispatch keeps things tidy:

php
$renderers = [
    'hero' => 'render_hero',
    'cta' => 'render_cta',
    'stat_row' => 'render_stat_row',
];

if ( have_rows( 'page_builder' ) ) {
    while ( have_rows( 'page_builder' ) ) {
        the_row();
        $layout = get_row_layout();
        if ( isset( $renderers[ $layout ] ) && function_exists( $renderers[ $layout ] ) ) {
            call_user_func( $renderers[ $layout ] );
        }
    }
}

function render_hero() {
    $heading = get_sub_field( 'heading' );
    printf( '<h1>%s</h1>', esc_html( $heading ) );
}

I rarely use this pattern in practice; template parts are simpler and easier for new team members to find.

Why get_row_layout might return nothing

Three causes:

  1. You are outside the have_rows() loop. get_row_layout() only works in row context. Calling it elsewhere returns false.
  2. You called have_rows() but forgot the_row(). Without the_row(), the row pointer never advances and get_row_layout() returns false. Covered in detail in ACF Flexible Content Loop Not Working? Here is the Fix.
  3. The field is not a Flexible Content field. get_row_layout() only returns a value for Flexible Content fields. For Repeater rows there is no layout name; the row is just an indexed position.

Quick diagnostic:

php
while ( have_rows( 'page_builder' ) ) {
    the_row();
    $layout = get_row_layout();
    if ( ! $layout ) {
        // Bail and figure out why
        error_log( 'get_row_layout returned nothing' );
        continue;
    }
    // ...
}

Reading the layout name without entering the loop

If you want to know which layouts a Flexible Content field contains for a post WITHOUT actually rendering them, read the field as an array and pluck the acf_fc_layout keys:

php
$rows = get_field( 'page_builder', $post_id );
if ( $rows ) {
    $layout_names = array_column( $rows, 'acf_fc_layout' );
    // $layout_names === ['hero', 'cta', 'stat_row']
}

Useful for analytics ("which layouts get used most often across the site?") and for cache invalidation logic ("if this page contains a pricing layout, clear the pricing cache too"). For the WP-CLI patterns to run audits like that across thousands of posts, see Using AI with WP-CLI for Faster WordPress Operations.

The acf_fc_layout key is also what you write when persisting Flexible Content data via update_field(), covered in How to Update ACF Fields Programmatically. Same key for both directions.

Sources

Authoritative references this article was fact-checked against.

TagsWordPressACFFlexible Content

Found this useful? Pass it on.

Copied

Ishan Karunaratne

Tech Architect · Software Engineer · AI/DevOps

Tech architect and software engineer with 20+ years building software, Linux systems, and DevOps infrastructure, and lately working AI into the stack. Currently Chief Technology Officer at a healthcare tech startup, which is where most of these field notes come from.

Keep reading

Related posts

How to Get Reliable JSON from an LLM

Get reliable JSON out of an LLM with native structured-output modes (Anthropic tool use, OpenAI Structured Outputs, Gemini schema), plus Zod / Pydantic validation as a fallback.