Displaying ACF Repeater Field on the Front
Table of Contents
1. Overview
ACF Repeater is one of the most popular and powerful field types in the Advanced Custom Fields plugin. As well as being one of the most complicated for developers and web creators to display.
Besides the extra layer of complexity, the repeater adds to the data itself; repeater items are often used as a source for lists, grids, or sliders, requiring extra effort compared to the linear field types. There are several different approaches to present the repeater data on the front, each one with its own pros and cons.
This article covers only displaying Repeater field values. If you're looking on how to update the Repeater field programatically, or query by it, read this article.
In the following chapters, we will review the most popular ways to display the Repeater field, while below is a short comparison to help you make the decision on the approach:
| Display approach | Pros | Cons |
|---|---|---|
| Advanced Views | 1. universal - works with any theme or page builder 2. fast - automates the groundwork and data fetching 3. flexible - direct access to the template code | 1. basic HTML/CSS knowledge is required to customize the templates 2. extra learning - some time to learn the tool |
| Specific page builder integration | 1. beginner-friendly - no code 2. fast - drag and drop UI | 1. builder-specific - bound to theme/builder, doesn't work everywhere 2. limited customization - no direct to the markup 3. may hurt SEO & page loading - elements often bloatwared |
| Custom PHP code | 1. universal - works with any theme or page builder 2. flexible - direct access to the template code | 1. PHP and WP-specific knowledge is required 2. time-consuming - a lot of repeating groundwork |
2. Displaying ACF Repeater using Advanced Views
Advanced Views is a framework to display content with full control over selection and layout. It's lightweight and compatible with any theme or page builder. Using Advanced Views is a healthy middle ground between drag-drop page builders with limited customization and no HTML access, and custom PHP coding, giving full control, but requiring a lot of repeating groundwork.
Note: AVF: Lite edition is free and supports all plain ACF field types, but Repeater is a multi-level field, supported only in the AVF: Pro edition. The good news that Pro contains a lot of useful features, including an option for custom Gutenberg blocks, which allows to turn any element into the Gutenberg block without any React coding.
2.1) List display for the Repeater field
The simplest way to display items is to place them in a single row.

In this example, our repeater contains a Text field with the label name, and a File field, with the attachment name.
- Navigate to the Layouts section and create a new Layout.
- Select the Repeater field in the Fields tab. In the field settings, go to the Subfields tab, select the subfields you want to display (as shown on the AVF Repeater Docs page)
- Save the Layout; a template will be automatically generated. You can copy and modify this template as needed.
- To integrate it into your page, paste the generated shortcode or use the Custom Gutenberg block option.
The great news is that AVF automatically picks up the field data, including the return format, and loads it into the template, allowing you to focus on the layout itself.
For this example, we used the following template:
{% if repeater.value %}
<div class="acf-view__repeater">
{% for item in repeater.value %}
<div class="acf-view__item">
{% if item.label.value %}
<p class="acf-view__item-label">
{{ item.label.value }}
</p>
{% endif %}
{% if item.attachment.value %}
<a class="acf-view__item-attachment" target="{{ item.attachment.target }}"
href="{{ item.attachment.value }}" download='{{ item.attachment.title }}'>
{{ item.attachment.linkLabel|default(item.attachment.title) }}
</a>
{% endif %}
</div>
{% endfor %}
</div>
{% endif %}
And also a bit of CSS to get the desired look. You should it to the CSS code field of your Layout and the framework will automatically enqueue it on the target pages:
#view__repeater {
display: flex;
gap: 20px;
}
#this__item {
display: flex;
flex-direction: column;
gap: 10px;
border: 1px solid gray;
border-radius: 10px;
}
Loading from different locations: To load a field from different locations (e.g., user profile), use the object-id shortcode argument.
2.2) Grid display for the Repeater field
The CSS grid feature is the simplest way to display Repeater items as a grid.

In the following example, we have the Title (Text field), Image (Image field), Price (Text field), and Year (Number field) subfields.
Create a Layout and select the Repeater field in the Fields tab. In the field settings, go to the Subfields tab, select the subfields you want to display (as shown on the AVF Repeater Docs page), and then click the Publish button. Your template will look like this:
{% if repeater.value %}
<div class="acf-view__repeater">
{% for item in repeater.value %}
<div class="acf-view__item">
{% if item.title.value %}
<h3 class="acf-view__item-title">
{{ item.title.value }}
</h3>
{% endif %}
{% if item.image.value %}
<img class="acf-view__item-image" src="{{ item.image.value }}" width="{{ item.image.width }}"
height="{{ item.image.height }}"
alt="{{ item.image.alt }}" decoding="{{ item.image.decoding }}"
loading="{{ item.image.loading }}"
srcset="{{ item.image.srcset }}" sizes="{{ item.image.sizes }}">
{% endif %}
{% if item.price.value %}
<p class="acf-view__item-price">
{{ item.price.value }}
</p>
{% endif %}
{% if item.year.value %}
<p class="acf-view__item-year">
{{ item.year.value }}
</p>
{% endif %}
</div>
{% endfor %}
</div>
{% endif %}
Now you can add the following CSS to the CSS code field of your Layout:
#view__repeater {
display: grid;
grid-template-columns: 1fr;
gap: 20px;
}
@media screen and (min-width:992px) {
#view__repeater {
grid-template-columns: repeat(2, 1fr);
}
}
2.3) Slider display for the Repeater field
A slider is an excellent way to present repeater items dynamically. To create a slider, you'll need to choose and implement a JavaScript library. In this example, we’ll use Splide.

In this case, we have the description (WYSIWYG field), name (Link field), and position (WYSIWYG field) subfields.
The simplest way to turn the repeater into the slider is to use Advanced Views, as the framework comes with pre-configured Slider, Masonry, and Image gallery libraries.
After adding the repeater field to the target Layout, change the 'Enable Slider' option to 'Splide v4' and press the Save button, as described on the AVF Repeater Docs page. The framework will automatically change the field markup to incorporate the necessary classes, and add the default JS instance:
var repeater = this.querySelector('.acf-view__repeater');
if (repeater_inner) {
/* https://splidejs.com/guides/options/ */
new Splide(repeater, {
type: 'loop',
perPage: 1,
perMove: 1,
}).mount();
}
Now you can customize the settings according to your needs, using any available Splide options.
2.4) Advanced display cases
Displaying nested repeater
A nested repeater refers to having a Repeater field within another Repeater field. To display nested repeaters, you'll need to first handle the outer repeater rows, and then process the nested repeater values using an additional loop.
AVF supports nested repeaters with no restrictions on the depth of nesting. To display a nested repeater, follow the steps, mentioned in the AVF Nested Repeater guide:
- Create the Parent Layout:
Go to the Layouts section and create a new 'Parent' Layout.
Select the target repeater field in the Fields tab and save the Layout. - Create the Child Layout:
Create another Layout, this will be the 'Child' Layout.
In the 'Parent Group' field of this Layout, choose the target repeater field that is nested within the Parent Layout and save the Layout. - Configure the Parent Layout:
Open the 'Parent' Layout.
In the repeater field settings of the Parent Layout, select the 'Child' Layout you just created from the 'Layout' setting and save the Parent Layout.
That’s it! You can now use the 'Parent' Layout wherever needed, and it will display both the outer repeater fields and the nested inner repeater. Also, you can customize the template further and add CSS/JS as usual.
Displaying a random repeater row
Since the Repeater field's response is an array, you can use built-in PHP functions to pick a random row.
Use the random function as shown on the AVF Repeater Docs page:
{% set randomItem = random(repeater.value) %}
{# todo work with the item as usually: randomItem.subfield.value #}
Using repeater rows sorting
If you have a Repeater field with subfields like "Title" and "Year" and you want to sort the rows by the "Year" field in ascending order, you can use PHP's sort functions. This allows you to sort the rows based on a specific subfield value.
Use the sort filter as shown on the AVF Repeater Docs page:
{% for item in repeater.value|sort((a, b) => a.year <=> b.year) %}
{# todo print item fields #}
{% endfor %}
3. Displaying ACF Repeater using page builders
Here is a snapshot of the built-in features offered by some of the most popular WordPress themes and page builders for displaying ACF Repeater fields:
| Theme/Builder | ACF-related feature | ACF Repeater type support |
|---|---|---|
| Astra | - | no |
| Avada | Dynamic Content | yes |
| Beaver | ACF Module | yes |
| Bricks | Dynamic data | yes |
| Divi | ACF Module | yes |
| Elementor | Dynamic Tags | no (not declared) |
| GeneratePress | - | no |
| Gutenberg | - | no |
| Kadence | Dynamic Content | yes |
| OceanWP | - | no |
| Visual Composer | Dynamic Content | no (not declared) |
| WPBackery | - | no |
While this list may seem brief, many themes come with their own page builders. Check your theme’s documentation for guidance on displaying ACF Repeater fields, or refer to the universal methods outlined in this article for a more flexible approach.
4. Displaying ACF Repeater field using PHP code
ACF functions and their responses are essentially wrappers around built-in WordPress features. This means that to load and display field values, you need to be familiar not only with ACF but also with core WordPress classes and functions. Despite all the flexibility, writing markup from scratch and manually escaping data can be time-consuming.
4.1) List display for the Repeater field
The simplest way to display items is to place them in a single row.

In this example, our repeater contains a Text field with the label name, and a File field, with the attachment name.
To get the Repeater data, you need to use the ACF get_field() function. As the second argument, you need to specify the source to load the value from. Don't forget about security, and escape the output as shown below:
// Get the Repeater field data
$repeater_field = get_field('your_repeater_field_name'); // from the current post
$repeater_field = get_field('your_repeater_field_name', 10); // from a specific post by ID
$repeater_field = get_field('your_repeater_field_name', 'option'); // from the options page
$repeater_field = get_field('your_repeater_field_name', 'user_1'); // from the user by ID
$repeater_field = get_field('your_repeater_field_name', 'category_2'); // from the category term with ID 2
$repeater_field = get_field('your_repeater_field_name', 'genre_3'); // from the custom genre term with ID 3
// Check if the repeater field has data
if ( $repeater_field ) {
// items wrapper
echo '<div class="repeater">';
// Loop through each row in the Repeater field
foreach ( $repeater_field as $row ) {
// Get sub-field values
$label = $row['label']; // Text sub-field
$attachment = $row['attachment']; // File sub-field
// Get attachment details (URL, title, etc.)
$attachment_url = isset( $attachment['url'] ) ? esc_url( $attachment['url'] ) : '';
$attachment_title = isset( $attachment['title'] ) ? esc_attr( $attachment['title'] ) : '';
// Output the label
printf( '<div class="repeater__item"><p class="repeater__label">%s</p>', esc_html( $label ) );
// Output the attachment link if available
if ( $attachment_url ) {
printf(
'<a class="repeater__file" href="%s" download="">%s</a>',
esc_url($attachment_url),
esc_html($attachment_title)
);
}
// Close the repeater item div
echo '</div>';
}
// Close the items wrapper div
echo '</div>';
}
Now we need to add some CSS to get the desired look. You should it to the style.css of your theme. However, by default, this code will be global and appear on all the site pages.
.repeater {
display: flex;
gap: 20px;
}
.repeater__item {
display: flex;
flex-direction: column;
gap: 10px;
border: 1px solid gray;
border-radius: 10px;
}
4.2) Grid display for the Repeater field
The CSS grid feature is the simplest way to display Repeater items as a grid.

In the following example, we have the Title (Text field), Image (Image field), Price (Text field), and Year (Number field) subfields.
Add the following code to the target theme template:
// Get the Repeater field data
$repeater_field = get_field( 'your_repeater_field_name' );
// Check if the repeater field has data
if ( $repeater_field ) {
// Items wrapper
echo '<div class="repeater">';
// Loop through each row in the Repeater field
foreach ( $repeater_field as $row ) {
// Get sub-field values
$title = $row['title'] ?? ''; // Text sub-field
$image = $row['image'] ?? ''; // Image sub-field
$price = $row['price'] ?? ''; // Text sub-field
$year = $row['year'] ?? ''; // Number sub-field
// Get image ID and details
$image_id = isset( $image['ID'] ) ? intval( $image['ID'] ) : 0;
$image_alt = isset( $image['alt'] ) ? esc_attr( $image['alt'] ) : 'Image';
// Output the item
echo '<div class="repeater__item">';
// Output the title
if ( $title ) {
printf( '<h3 class="repeater__title">%s</h3>', esc_html( $title ) );
}
// Output the image using wp_get_attachment_image()
if ( $image_id ) {
echo wp_get_attachment_image( $image_id, 'full', false, array( 'alt' => $image_alt ) );
}
// Output the price
if ( $price ) {
printf( '<p class="repeater__price">%s</p>', esc_html( $price ) );
}
// Output the year
if ( $year ) {
printf( '<p class="repeater__year">%s</p>', esc_html( $year ) );
}
// Close the item div
echo '</div>';
}
// Close the items wrapper div
echo '</div>';
}
And the following CSS to the style.css of your theme:
.repeater {
display: grid;
grid-template-columns: 1fr;
gap: 20px;
}
@media screen and (min-width:992px) {
.repeater {
grid-template-columns: repeat(2, 1fr);
}
}
4.3) Slider display for the Repeater field
A slider is an excellent way to present repeater items dynamically. To create a slider, you'll need to choose and implement a JavaScript library. In this example, we’ll use Splide.

In this case, we have the description (WYSIWYG field), name (Link field), and position (WYSIWYG field) subfields.
In the manual coding case, you need to download the CSS and JS code of the Splide library to your theme. Then, print the gallery in the target template with the necessary splide classes:
// Get the Repeater field data
$repeater_field = get_field( 'your_repeater_field_name' );
// Check if the repeater field has data
if ( $repeater_field ) {
// Wrapper for the Splide slider
echo '<div class="splide">';
// Items wrapper for Splide slider
echo '<div class="splide__track">';
echo '<ul class="splide__list">';
// Loop through each row in the Repeater field
foreach ( $repeater_field as $row ) {
// Get sub-field values using null coalescing operator
$description = $row['description'] ?? ''; // WYSIWYG sub-field
$name = $row['name'] ?? ''; // Link sub-field
$position = $row['position'] ?? ''; // WYSIWYG sub-field
// Output the item
echo '<li class="splide__slide">';
// Output the description
if ( $description ) {
printf( '<div class="slider__description">%s</div>', wp_kses_post( $description ) );
}
// Output the name as a link
if ( $name ) {
printf( '<a class="slider__name" href="%s">%s</a>', esc_url( $name['url'] ), esc_html( $name['title'] ) );
}
// Output the position
if ( $position ) {
printf( '<div class="slider__position">%s</div>', wp_kses_post( $position ) );
}
// Close the slide item
echo '</li>';
}
// Close the Splide list and track wrapper divs
echo '</ul>';
echo '</div>';
// Close the general Splide wrapper div
echo '</div>';
}
Then enqueue the library's CSS using the wp_enqueue_style function, and add the following JS to your theme's script.js:
import '/wp-content/themes/YOUR_THEME/assets/js/splide.js';
document.addEventListener('DOMContentLoaded', function () {
var slider = document.body.querySelector('.splide');
if (slider) {
/* https://splidejs.com/guides/options/ */
new Splide(slider, {
type: 'loop',
perPage: 1,
perMove: 1,
}).mount();
}
});
Now you can customize the settings according to your needs, using any available Splide options.
4.4) Advanced display cases
Displaying nested repeater
A nested repeater refers to having a Repeater field within another Repeater field. To display nested repeaters, you'll need to first handle the outer repeater rows, and then process the nested repeater values using an additional loop.
Let's modify the code from our Grid example, reviewed above, to turn the single year field into the year's repeater:
// Get the Repeater field data
$repeater_field = get_field( 'your_repeater_field_name' );
// Check if the repeater field has data
if ( $repeater_field ) {
// Items wrapper
echo '<div class="repeater">';
// Loop through each row in the Repeater field
foreach ( $repeater_field as $row ) {
// Get sub-field values
$title = $row['title'] ?? ''; // Text sub-field
$image = $row['image'] ?? ''; // Image sub-field
$price = $row['price'] ?? ''; // Text sub-field
// Get nested repeater field data (if any)
$nested_repeater = $row['nested_years_repeater'] ?? []; // Replace with your nested repeater field name
// Get image ID and details
$image_id = $image['ID'] ?? 0;
$image_alt = $image['alt'] ?? 'Image';
// Output the item
echo '<div class="repeater__item">';
// Output the title
if ( $title ) {
printf( '<h3 class="repeater__title">%s</h3>', esc_html( $title ) );
}
// Output the image using wp_get_attachment_image()
if ( $image_id ) {
echo wp_get_attachment_image( $image_id, 'full', false, [ 'alt' => $image_alt ] );
}
// Output the price
if ( $price ) {
printf( '<p class="repeater__price">%s</p>', esc_html( $price ) );
}
// Output the nested repeater
if ( $nested_repeater ) {
echo '<div class="nested-repeater">';
foreach ( $nested_repeater as $nested_row ) {
$nested_year = $nested_row['nested_year'] ?? ''; // Replace with your nested sub-field name
// Output the nested repeater item
echo '<div class="nested-repeater__item">';
// Output the nested year
if ( $nested_year ) {
printf( '<p class="nested-repeater__year">%s</p>', esc_html( $nested_year ) );
}
// Close the nested item div
echo '</div>';
}
// Close the nested repeater wrapper div
echo '</div>';
}
// Close the item div
echo '</div>';
}
// Close the items wrapper div
echo '</div>';
}
Displaying a random repeater row
Since the Repeater field's response is an array, you can use built-in PHP functions to pick a random row.
Here can use the array_rand function in PHP to pick a random row index. Once you've selected a random row, you can process and display it as usual. Here's an example to illustrate this:
// Get the Repeater field data
$repeater_field = get_field( 'your_repeater_field_name' );
// Check if the repeater field has data
if ( $repeater_field ) {
// Pick a random row index
$random_index = array_rand( $repeater_field );
$random_row = $repeater_field[ $random_index ];
// Get sub-field values from the random row
$description = $random_row['description'] ?? ''; // WYSIWYG sub-field
$name = $random_row['name'] ?? ''; // Link sub-field
$position = $random_row['position'] ?? ''; // WYSIWYG sub-field
// todo create the target HTML
}
Using repeater rows sorting
If you have a Repeater field with subfields like "Title" and "Year" and you want to sort the rows by the "Year" field in ascending order, you can use PHP's sort functions. This allows you to sort the rows based on a specific subfield value.
Here we can use the PHP usort function:
// Get the Repeater field data
$repeater_field = get_field( 'your_repeater_field_name' );
// Check if the repeater field has data
if ( $repeater_field ) {
// Sort the rows by the 'year' subfield in ascending order
usort( $repeater_field, function ( $a, $b ) {
$year_a = $a['year'] ?? 0; // Handle missing or null values by defaulting to 0
$year_b = $b['year'] ?? 0; // Handle missing or null values by defaulting to 0
return $year_a <=> $year_b; // Ascending order comparison
} );
// todo output the rows as usually
}
Tip: To change the sorting order to descending, you simply need to adjust the comparison logic within the usort function. For descending order, you can reverse the comparison by swapping the variables, so placing $year_b at the left side.
Thank you for reading! to our monthly newsletter to stay updated on the latest WordPress news and useful tips.
Stuck with development or facing an issue?
WPLake offers affordable on-demand website development and design.
No matter the size of your project - contact us now, and we'll get it done for you!
Content links (57)
20.
splidejs.com50.
splidejs.com