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
php
[
'id' => 'team_members',
'type' => 'repeater',
'title' => __('Team Members', 'your-textdomain'),
'subtitle' => __('Add team members to display on the About page', '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'),
],
[
'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 — same structure as regular fields, but without section-level keys |
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
Most standard field types are supported as sub-fields inside a Repeater row:
text, textarea, number, slider, select, button_set, radio, checkbox, toggle, color, image, icon, url, email
Complex nested types such as repeater, typography, spacing, border, and dimensions are not supported as sub-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.
php
$members = themeplus_get_option( 'team_members', [] );
// Returns:
// [
// ['name' => 'Alice', 'role' => 'Designer', 'photo' => 42, 'bio' => '...'],
// ['name' => 'Bob', 'role' => 'Developer', 'photo' => 43, 'bio' => '...'],
// ]
Usage Examples
Rendering team members
php
$members = themeplus_get_option( 'team_members', [] );
if ( ! empty( $members ) ) {
echo '<div class="team-grid">';
foreach ( $members as $member ) {
$name = $member['name'] ?? '';
$role = $member['role'] ?? '';
$photo_id = $member['photo'] ?? '';
$bio = $member['bio'] ?? '';
echo '<div class="team-member">';
if ( $photo_id ) {
echo wp_get_attachment_image( $photo_id, 'medium', false, ['class' => 'team-member__photo'] );
}
echo '<h3 class="team-member__name">' . esc_html( $name ) . '</h3>';
echo '<p class="team-member__role">' . esc_html( $role ) . '</p>';
echo '<p class="team-member__bio">' . esc_html( $bio ) . '</p>';
echo '</div>';
}
echo '</div>';
}
FAQ accordion
php
[
'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'),
],
],
]
php
$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
php
[
'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
php
[
'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
php
[
'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],
],
Notes
- Always check
! empty( $rows )before looping — the field returns an empty array when no rows have been added. - Always use the
??null coalescing operator when reading sub-field values from each row — individual keys may be absent if a row was saved before a sub-field was added. - Sub-field IDs must be unique within the repeater but do not need to be globally unique across the entire options panel.
- Rows are returned in the order the user arranged them in the panel — use this for drag-and-drop ordered content like feature lists or pricing tiers.
- Avoid registering too many sub-fields per row — five to eight sub-fields per row keeps the panel usable. For complex structured data, consider splitting into multiple repeaters.