Overview
The Repeater field renders a list of rows where each row contains a defined set of sub-fields. The user can add, remove, and reorder rows. It returns an array of associative arrays — one per row — each containing the values of its sub-fields. Use it for team members, testimonials, pricing table rows, FAQ items, feature lists, custom menu items, or any other structured repeatable content in your theme.
Field Registration
[
'id' => 'team_members',
'type' => 'repeater',
'title' => __( 'Team Members', 'your-textdomain' ),
'subtitle' => __( 'Add team members to display on the About page', 'your-textdomain' ),
'min' => 0,
'max' => 12,
'button_label' => __( 'Add Team Member', 'your-textdomain' ),
'fields' => [
[
'id' => 'name',
'type' => 'text',
'title' => __( 'Name', 'your-textdomain' ),
],
[
'id' => 'role',
'type' => 'text',
'title' => __( 'Role', 'your-textdomain' ),
],
[
'id' => 'photo',
'type' => 'image',
'title' => __( 'Photo', 'your-textdomain' ),
// Returns: { id, url, width, height, alt, title } — same as the Image field
],
[
'id' => 'bio',
'type' => 'textarea',
'title' => __( 'Bio', 'your-textdomain' ),
],
],
]Field Options
| Option | Type | Required | Description |
|---|---|---|---|
id | string | ✅ | Unique field identifier — used as the option key |
type | string | ✅ | Must be repeater |
title | string | ✅ | Label shown above the repeater |
subtitle | string | — | Smaller descriptive text shown below the label |
desc | string | — | Help text shown below the repeater |
fields | array | ✅ | Array of sub-field definitions |
min | int | — | Minimum number of rows — user cannot delete below this count. Default: 0 |
max | int | — | Maximum number of rows — Add Row button is hidden when this count is reached. Default: unlimited |
button_label | string | — | Custom label for the Add Row button. Default: 'Add Row' |
default | array | — | Array of default row data — each item is an associative array matching the sub-field IDs |
required | array | — | Conditional logic rules — see Conditional Logic |
Supported Sub-field Types
The Repeater passes each sub-field directly through FieldRenderer — any registered field type works as a sub-field with two exceptions:
Fully supported:
text, textarea, number, spinner, slider, select, button_set, radio, checkbox, select_image, toggle, switch, color, gradient_picker, image, gallery, icon, typography, dimensions, spacing, border, background, link, date_picker, social_media, code_editor, group
Display-only (no stored value — usable for UI structure):
info, section, raw
Not supported as a sub-field:
repeater — nesting a repeater inside a repeater is not supported.
Note: Complex sub-fields like image, gallery, link, background, and group return their full structured array shapes inside each row — the same shapes as when used as standalone fields.
Return Value
Type: array
Returns an indexed array of associative arrays — one per row — each containing the sub-field values keyed by sub-field ID. Returns an empty array if no rows have been added.
$members = themeplus_get_option( 'team_members', [] );
// Returns:
// [
// [
// 'name' => 'Alice',
// 'role' => 'Designer',
// 'photo' => [
// 'id' => 42,
// 'url' => 'https://example.com/wp-content/uploads/alice.jpg',
// 'width' => 300,
// 'height' => 300,
// 'alt' => 'Alice',
// 'title' => 'Alice',
// ],
// 'bio' => 'Alice is a senior designer...',
// ],
// [
// 'name' => 'Bob',
// 'role' => 'Developer',
// 'photo' => [], // empty array when no image selected
// 'bio' => 'Bob is a backend developer...',
// ],
// ]Usage Examples
Rendering team members
$members = themeplus_get_option( 'team_members', [] );
if ( ! empty( $members ) ) {
echo '';
foreach ( $members as $member ) {
$name = $member['name'] ?? '';
$role = $member['role'] ?? '';
$photo = $member['photo'] ?? []; // array, not an ID
$bio = $member['bio'] ?? '';
echo '';
if ( ! empty( $photo['url'] ) ) {
echo '';
}
echo '' . esc_html( $name ) . '';
echo '' . esc_html( $role ) . '';
echo '' . esc_html( $bio ) . '';
echo '';
}
echo '';
}FAQ accordion
[
'id' => 'faq_items',
'type' => 'repeater',
'title' => __('FAQ Items', 'your-textdomain'),
'fields' => [
[
'id' => 'question',
'type' => 'text',
'title' => __('Question', 'your-textdomain'),
],
[
'id' => 'answer',
'type' => 'textarea',
'title' => __('Answer', 'your-textdomain'),
],
],
]$faq_items = themeplus_get_option( 'faq_items', [] );
if ( ! empty( $faq_items ) ) {
echo '<dl class="faq">';
foreach ( $faq_items as $item ) {
$question = $item['question'] ?? '';
$answer = $item['answer'] ?? '';
if ( ! $question ) continue;
echo '<dt class="faq__question">' . esc_html( $question ) . '</dt>';
echo '<dd class="faq__answer">' . esc_html( $answer ) . '</dd>';
}
echo '</dl>';
}Testimonials slider
[
'id' => 'testimonials',
'type' => 'repeater',
'title' => __('Testimonials', 'your-textdomain'),
'fields' => [
[
'id' => 'quote',
'type' => 'textarea',
'title' => __('Quote', 'your-textdomain'),
],
[
'id' => 'author',
'type' => 'text',
'title' => __('Author Name', 'your-textdomain'),
],
[
'id' => 'company',
'type' => 'text',
'title' => __('Company / Role', 'your-textdomain'),
],
[
'id' => 'avatar',
'type' => 'image',
'title' => __('Avatar', 'your-textdomain'),
],
[
'id' => 'rating',
'type' => 'select',
'title' => __('Rating', 'your-textdomain'),
'default' => '5',
'options' => [
'5' => '★★★★★',
'4' => '★★★★☆',
'3' => '★★★☆☆',
],
],
],
]With a default row
[
'id' => 'social_links_custom',
'type' => 'repeater',
'title' => __('Custom Social Links', 'your-textdomain'),
'default' => [
['label' => 'GitHub', 'url' => '', 'icon' => 'fa-brands fa-github'],
],
'fields' => [
[
'id' => 'label',
'type' => 'text',
'title' => __('Label', 'your-textdomain'),
],
[
'id' => 'url',
'type' => 'text',
'title' => __('URL', 'your-textdomain'),
],
[
'id' => 'icon',
'type' => 'icon',
'title' => __('Icon', 'your-textdomain'),
'default' => 'fa-brands fa-github',
],
],
]With a conditional field
[
'id' => 'enable_testimonials',
'type' => 'toggle',
'title' => __('Enable Testimonials Section', 'your-textdomain'),
'default' => false,
],
[
'id' => 'testimonials',
'type' => 'repeater',
'title' => __('Testimonials', 'your-textdomain'),
'fields' => [
['id' => 'quote', 'type' => 'textarea', 'title' => __('Quote', 'your-textdomain')],
['id' => 'author', 'type' => 'text', 'title' => __('Author', 'your-textdomain')],
],
'required' => ['enable_testimonials', '==', true],
],