Repeating a Section for Every Row

Use {EACH} in a wiki to write a clause once per beneficiary, asset or guardian – including numbering, the previous row and nested lists.

Help CentreForms & Data

Repeating a Section for Every Row

When a form collects a list – beneficiaries, assets, guardians, executors – you cannot know in advance how many rows there will be. {EACH:…} writes the same passage once for every row, substituting that row's values each time.


Table of Contents

  1. The Basic Shape
  2. Referring to a Column
  3. The Row Number
  4. First, Last and Separators
  5. The Previous Row
  6. Conditions Per Row
  7. Nested Lists
  8. Blank Rows Are Ignored
  9. Common Mistakes

The Basic Shape

{EACH:list_name}
  …content repeated once per row…
{/EACH}

list_name is the variable name of the list field on the linked form. Every {EACH:…} needs a matching {/EACH}.

{EACH:beneficiaries}
  I bequeath {.share} of my estate to {.full_name}.
{/EACH}

Referring to a Column

Inside the loop, put a dot in front of a column name to get that column from the current row:

TokenMeans
{.full_name}The current row's full_name column
{full_name}A top-level form field called full_namenot the row

That dot is the single most important character in this article. Leaving it off is the most common reason a loop renders the same value on every row, or nothing at all.

Column names may contain spaces and are matched without regard to capitalisation, so {.full name} will still find a column called Full Name. Matching the form exactly is still better – the sidebar will tell you when the case differs.


The Row Number

{#} is the current row's number, starting at 1.

{EACH:assets}
  {#}. {.description}
{/EACH}

It also works inside conditions, which is what makes position-aware wording possible:

{EACH:heirs}{IF:{#}>1}, {/IF}{.full_name}{/EACH}

That renders Anna, Ben, Cara – a comma before every name except the first.


First, Last and Separators

You wantWrite
The first row only{IF:{#}=1}…{/IF}
Everything except the first{UNLESS:{#}=1}…{/UNLESS}
A different opening for row 1{IF:{#}=1}Firstly, {ELSE}; and {/IF}
The second row onwards{IF:{#}>1}…{/IF}

The Previous Row

{PREV.column} gives the same column from the previous row. It exists for cascades – each item naming the one before it:

{EACH:heirs}
  {IF:{#}>1}failing {PREV.full_name}, then {.full_name};{/IF}
{/EACH}

With heirs John, Mary and Peter that produces failing John, then Mary; failing Mary, then Peter; – the substitutionary wording a will needs, from one line of template.

Three things to know:

  • The first row has no previous row, so {PREV.column} produces nothing there. Guard it with {IF:{#}>1} whenever the sentence only makes sense with a predecessor.
  • Blank rows do not count. "Previous" means the previous row that actually appeared, so it always agrees with {#}.
  • {IF:PREV.column} also works, and is false on the first row – a natural alternative guard.
Note: {PREV.column} always refers to the previous row of the loop it sits in. In nested loops that is the inner one.

Conditions Per Row

Conditions inside a loop can test the current row using the same leading dot:

{EACH:children}
  {.full_name}{IF:.is_minor} (a minor){/IF}
{/EACH}

This is also how per-row calculated fields are used – a calculated field with per row scope adds a column, and you read it exactly like a real one. See Calculated Fields in Wikis.


Nested Lists

Loops can contain loops. The inner loop runs in full for every row of the outer one, and both row scopes are available inside.

{EACH:sections}
  {#}. {.heading}
  {EACH:clauses}
    {../#}.{#} {.text}
  {/EACH}
{/EACH}

{../#} is the outer loop's row number – add another ../ for each further level out. The example numbers clauses 1.1, 1.2, 2.1 and so on.

When both lists have a column of the same name, the inner row wins inside the inner loop. Where the inner list has no such column, the outer row's value is used – so an inner loop can still reach an outer column it does not define itself.


Blank Rows Are Ignored

Users add a row and change their mind. A row where every value is empty – or where nothing was changed from the defaults the form offered – is dropped before the loop runs.

This matters more than it sounds:

  • Numbering has no gaps. Rows show as 1, 2, 3, not 1, 3.
  • COUNT() reports what a human would count.
  • {PREV.column} reaches the previous real row, never a blank one.

You do not need to guard against empty rows yourself.


Common Mistakes

SymptomUsual cause
The same value repeats on every rowA missing dot – {full_name} instead of {.full_name}.
{.column} appears as text in the PDFNo column of that name on this list. Check spelling against the form; the sidebar flags this.
Nothing renders at allThe list name after {EACH: does not match the form's list field, or the list is genuinely empty.
A per-row condition never firesThe condition is missing its leading dot, so it is looking for a top-level field.
The outer loop's content is cut shortAn inner {/EACH} is missing, so the wrong closing tag ended the outer block.
Two identical rows produced one paragraphDeliberate: consecutive rows that render identically are collapsed. Include something that differs – a row number or a name – if you want both.
Tip: Build the loop body as a plain paragraph first and confirm it repeats correctly with real data in Preview. Add conditions and numbering afterwards, one at a time.

Next: Calculated Fields in Wikis, or back to Wiki Logic.

See it working on your own data

Everything documented here ships with the platform – try the document tools free, or go live in 7 days.