Survey Design
Advanced Features
Advanced Repeats

This page covers advanced patterns for working with repeat groups in rtSurvey. For the basics of setting up a repeat group, see Grouping and Repeats.


Dynamic repeat count

By default, the enumerator decides how many times to repeat. You can fix the number of repetitions using repeat_count:

typenamelabelrepeat_count
begin_repeathousehold_membersHousehold member${num_members}
textmember_nameMember name
integermember_ageAge
end_repeat

The repeat runs exactly ${num_members} times, where num_members was collected earlier in the form. The enumerator cannot add or remove instances.


Indexed access: indexed-repeat()

Access a specific repeat instance's field value from outside the repeat group using indexed-repeat(repeatedField, repeatGroup, index):

typenamelabelcalculation
calculatefirst_nameindexed-repeat(${member_name}, ${household_members}, 1)
calculatesecond_nameindexed-repeat(${member_name}, ${household_members}, 2)

This is useful for building summary fields or referencing the "primary" member's data after the repeat.


Current instance position: index()

Inside a repeat group, index() returns the 1-based position of the current instance. Use it to label each repetition or create unique identifiers:

typenamelabel
begin_repeatplotsPlot
noteplot_labelPlot number ${index()}
textplot_idPlot ID
end_repeat

Referencing fields in the same instance

Within a repeat, use ${fieldname} to reference another field in the same repeat instance. There is no need for indexed-repeat() within the same loop:

typenamelabelrelevant
begin_repeatmembersMember
textmember_nameName
integermember_ageAge
textschool_nameSchool name${member_age} < 18
end_repeat

Referencing parent fields from inside a repeat

Fields outside (above) the repeat group can be referenced normally with ${fieldname}:

typenamelabel
textvillageVillage name
begin_repeatplotsAgricultural plot
noteplot_contextPlots in ${village}
end_repeat

Summarising repeat data

Use repeat aggregate functions outside the repeat group to summarise:

FunctionExampleDescription
count(group)count(${household_members})Number of instances
sum(field)sum(${loan_amount})Sum of a numeric field
min(field)min(${member_age})Minimum value
max(field)max(${member_age})Maximum value
join(sep, field)join(', ', ${member_name})Comma-separated list
count-if(group, expr)count-if(${members}, ${member_age} < 18)Conditional count
sum-if(field, expr)sum-if(${loan_amount}, ${loan_amount} > 500)Conditional sum
join-if(sep, field, expr)join-if(', ', ${name}, ${age} >= 18)Conditional join

Example: Household summary

typenamelabelcalculation
integernum_membersHow many members?
begin_repeatmembersMember${num_members}
textmember_nameName
integermember_ageAge
end_repeat
calculatetotal_memberscount(${members})
calculatechildren_countcount-if(${members}, ${member_age} < 18)
calculateadult_namesjoin-if(', ', ${member_name}, ${member_age} >= 18)
notesummary${total_members} members; ${children_count} under 18. Adults: ${adult_names}

Nested repeats

A repeat group can contain another repeat group. Use this carefully — nested repeats add complexity and can be confusing for enumerators.

typenamelabel
begin_repeathouseholdsHousehold
texthh_idHousehold ID
begin_repeathh_membersMember
textmember_nameMember name
end_repeat
end_repeat

To reference a field in an outer repeat from the inner repeat, use ${fieldname} — it resolves to the nearest matching ancestor:

Inside the hh_members repeat, ${hh_id} returns the ID of the current household, not all households.


Rank inside repeat groups: rank-index()

When a rank field exists inside a repeat, use rank-index(instanceNumber, repeatedField) from outside to get the ordinal rank of a specific instance:

typenamelabelcalculation
calculatetop_scorerrank-index(1, ${score})

rank-index(1, ${score}) returns the instance index of the highest score.


1screen appearance on begin_repeat

Adding 1screen to the appearance column of a begin_repeat row displays all questions for each instance together on a single screen, rather than one question per screen.

typenamelabelappearance
begin_repeathousehold_membersHousehold member1screen
textmember_nameMember name
integermember_ageAge
end_repeat

When 1screen is set and there is no repeat_count and no add_repeat field inside the group, rtSurvey automatically renders a circular + button at the bottom of the screen so enumerators can add new instances without any extra configuration.

AppearanceOne screen per instanceDefault + button added
1screenYesYes (when no add_repeat child)
field-listYesNo

Use field-list when you want the grouped layout without the automatic add button. Use 1screen when you want both the grouped layout and a built-in way to add instances.


add_repeat custom add button

A note (or text) field placed inside the repeat group with appearance: add_repeat renders as a full-width "Add" button. The button label comes from the field's label column. When an add_repeat field is present, the default circular + button (from 1screen) is suppressed.

Place the add_repeat field as the last field inside the repeat group:

typenamelabelappearance
begin_repeatmembersMember1screen
textmember_nameMember name
integermember_ageAge
notebtn_add+ Add memberadd_repeat
end_repeat

Color customization

Pass a color code inside the appearance value to style the button:

Appearance valueResult
add_repeat <#04B404/>Green background, default text color
add_repeat 04B404-ffffffGreen background, white text

The first color is the background and the second (after the dash) is the text color. Omit the # when using the dash-separated form.

Icon variant

Use add_repeat icon (with a space) to render a compact circular icon button instead of the full-width button:

typenamelabelappearance
notebtn_add+ Addadd_repeat icon

Delete buttons: delete-repeat-current and delete-repeat-last

A note field inside the repeat group with appearance: delete-repeat-current or appearance: delete-repeat-last renders as a split delete button.

AppearanceAction
delete-repeat-currentDeletes the current (active) instance
delete-repeat-lastDeletes the last instance in the repeat

The default (non-icon) button is a split button: a trash icon on the left and the field's label text on the right, spanning the full width of the screen.

Icon variant

Append -icon to get a compact circular trash icon button:

AppearanceResult
delete-repeat-current-iconCircular trash icon, deletes current instance
delete-repeat-last-iconCircular trash icon, deletes last instance

Color customization

Color format is identical to add_repeat: use <#RRGGBB/> or RRGGBB-RRGGBB (background-text).

Full pattern example

The following repeat uses 1screen, a circular delete-current button at the top, and a full-width add button at the bottom:

typenamelabelappearance
begin_repeatmembersMember1screen
notebtn_delRemovedelete-repeat-current-icon
textmember_nameMember name
integermember_ageAge
notebtn_add+ Add memberadd_repeat
end_repeat

Best Practices

  1. Always use repeat_count when the number of repetitions is known in advance — it prevents enumerators from accidentally adding or removing instances.
  2. Keep repeat groups focused — a repeat with 20+ questions per instance is difficult to navigate.
  3. Name repeat groups clearly (e.g., household_members, not repeat1) — the name appears in function calls and exported data.
  4. Test with the maximum expected number of instances to verify performance.
  5. Use 1screen on begin_repeat to show all questions on one screen per instance, paired with an add_repeat note field inside the group to give enumerators a clearly labelled "Add" button.

Limitations

  • indexed-repeat() requires a valid index (1 to count of instances) — out-of-range indices return empty.
  • Nested repeats beyond 2 levels are not recommended and may cause display issues on some clients.
  • Aggregate functions (sum, count, etc.) operate on the entire repeat group — you cannot aggregate a subset of instances without *-if variants.