Repeater
Creating a repeater#
Add a Repeater field and define its Sub Fields: any field types, including another repeater. The other settings:
- Minimum / Maximum Rows: bounds on how many rows editors can add.
- Layout: how rows render in the admin: Block, Table or Row.
- Button Label: the text on the add button (defaults to "Add Row").
Editors drag rows to reorder, and duplicate or remove them per row.
Looping in templates#
The classic loop, familiar from WordPress meta APIs:
if ( ultivo_have_rows( 'team' ) ) : while ( ultivo_have_rows( 'team' ) ) : ultivo_the_row(); $name = ultivo_get_sub_field( 'name' ); endwhile; endif;
Or the Template API: ultivo_rows() returns an iterable, countable object with no global loop state, and an empty repeater simply iterates zero times:
foreach ( ultivo_rows( 'team' ) as $member ) { echo esc_html( $member['name'] ); }
Inside the classic loop, ultivo_get_row_index() returns the zero-based index of the current row. ultivo_the_sub_field( 'name' ) echoes a sub-field directly.
Adding rows in code#
ultivo_add_row() appends a row programmatically, keyed by sub-field name, and returns the new row count:
ultivo_add_row( 'team', [ 'name' => 'Jane' ] );
Storage#
The repeater's own meta key stores the row count; each sub-field value is stored as {name}_{index}_{subname}. A team repeater with a name sub-field and two rows saves:
team => 2 team_0_name => "Jane" team_1_name => "John"
This is the same pattern ACF uses, so migrated repeater data keeps working as-is.
Nested repeaters#
Repeaters nest: a sub-field can itself be a repeater. In the classic loop, call ultivo_have_rows() with the sub-repeater's name inside the outer loop. With the Template API, call ->rows( 'sub_name' ) on a row. Storage nests the same way: slides_0_buttons_1_label is the second button of the first slide.