← Documentation

Repeating Sections

Professional & Enterprise

A Repeating Section is a form element that displays related child-list items as an inline table inside the parent form. Users can add, edit, and delete rows directly — without leaving the form or opening a separate list. It’s ideal for line items, sub-tasks, contacts, or any one-to-many relationship you want to manage in place.

1. What it does

  • Shows child-list items in a table embedded in the parent form.
  • Lets users add, edit, and delete rows inline (subject to the operations you allow).
  • The child list can live in any site collection within the tenant.
  • Adds calculated columns and a totals row for live per-row and per-column roll-ups (see §4–§5).
  • Supports lookup columns and lookup-derived columns that surface extra fields from a selected item (see §6–§7).
  • Comes with a Custom JS callback (see §9) so you can react to the section’s data in code.

2. Inline-editable field types

Rows can be edited directly in the table for these field types:

  • Single-line text
  • Multiline plain text
  • Date/Time
  • Choice (single)
  • Yes/No (boolean)
  • Number
  • Currency
  • People picker
  • Lookup (single and multi-value)
  • Managed metadata

Field types not in this list can still be displayed as columns, but aren’t editable inline.

3. Setting it up (form builder)

  1. In the form builder, click + and add the Repeating Section element to a column.
  2. Open its configuration panel and set:
SettingWhat it controls
Child listThe list whose items are shown. May be on the current site or any site collection in the tenant. It must be a list, not a document library — rows are list items, and a library cannot take them, so libraries are not offered. For documents related to the item, use a vLookup, which can also upload them.
Parent link fieldThe column on the child list that ties a row to this form’s item. Single line of text, a single-value Lookup pointing at the parent, or a Number column holding the parent item’s ID. Multi-value Lookup columns are not offered — SharePoint cannot filter on them, so the section could never load its rows. For a text parent link you also pick the source field on the parent form that supplies the value: Text, Choice, or ID — the types that can be matched back reliably. On a new form, rows written with an ID source pick up the new item’s ID when you save. A column whose internal name starts with an underscore, such as _DFFSID, works as a parent link.
Columns (fields to display)Which fields appear as table columns, and their order.
Allowed operationsWhich of Add / Edit / Delete end users may perform.
Row constraintsMinimum and maximum number of rows allowed.
HeadingA custom heading shown above the table (supports MUI multi-language overrides).
Empty-state messageText shown when the section has no rows yet (supports MUI).
Column display titleOptional per-column header. Defaults to the live field title from the target list (shown as a placeholder in the panel), so a field renamed in SharePoint is reflected automatically unless you set a custom title. Supports [[variable:Name]] tokens — including on calculated columns, which have no underlying field — for multi-language headers driven by Custom JS.
Column prefix / suffixOptional free text shown immediately before and/or after each column’s value — e.g. a currency symbol (kr ) or a unit ( kg). Also applied to that column’s total in the totals row (see §5).
Predefined field valuesField/value pairs written to every row added through the section, without appearing as a column — the same idea as a vLookup’s prefill values. Tokens such as [[variable:Name]] resolve against the parent form when it is saved, which is what lets [[fieldValue:ID]] work on a new form — the parent item does not exist until then. Each value is converted to whatever the target column accepts, and a Lookup or Person column can be filled by item ID: use [[fieldValue:ID]] for the parent item, or a token ending in .LookupId for a lookup on the form. A Lookup needs an id rather than a title. Add only: an existing row is not restated, and a predefined value never overwrites a column the user can edit in the section, including one they deliberately cleared.
  1. Save the configuration.

The section’s element id is shown at the bottom of its configuration panel. It is the single value used everywhere: in rules, in Custom CSS, and as the elementId argument handed to the Custom JS callbacks in §9 — where it arrives in its prefixed form, repeatingSection_{id}.

4. Calculated columns

Add a display-only column whose value is computed live from other columns in the same row, using the same formula engine as the main form’s formula fields. Reference other columns with [InternalName] tokens — including lookup-derived columns (see §7) — and combine them with the Excel-like function library:

  • Logic: IF, AND, OR, NOT
  • Text: CONCATENATE and related text helpers
  • Date & number/math: date functions (e.g. DATEADD) and number/math functions (e.g. ROUND), plus the arithmetic operators + - * /
  • Recalculates as you type while adding or editing a row, and whenever a referenced lookup or lookup-derived value changes.
  • Display-only by default — computed as the table is drawn and not stored. It can be written to a real column as well; see Also save the result to below.

Formulas are edited in a dedicated Edit calculated column dialog — opened from the column’s read-only formula field, or via Add calculated column. The dialog has a multiline formula editor plus a live Available columns reference (which lists lookup-derived columns automatically) and a collapsible Available functions reference, and it validates the formula as you type.

Example: an Amount calculated column defined as [Quantity] * [UnitPrice] shows each line’s total in the table without storing it in the list.

Adding a currency symbol or unit? Give the column a prefix or suffix (§3) rather than building it into the formula with CONCATENATE. Prefix/suffix decorates the displayed text only, so the underlying value stays a clean number and the column’s Sum / Average / Min / Max total keeps working. A CONCATENATE-built label turns the value into text — which can’t be aggregated, so the totals row for that column silently produces nothing.

Also save the result to a real column

A calculated column is presentation only, which means its value cannot be reported on outside the form. The calculated column dialog therefore offers Also save the result to (optional): pick a column on the child list and the computed value is written there every time the row is added or edited through the form, on top of the live display value — so the number is available in views, exports, Power BI and flows.

  • Eligible targets are Text, Multiple lines of text, Number and Currency. Read-only columns, the parent link field, and any column editable in the same section are excluded — otherwise the saved result would silently overwrite what the user just typed.
  • A Number or Currency target receives the raw number; a text target receives the formatted string. The cosmetic prefix/suffix is deliberately not included, consistent with it never entering calculation or aggregation maths.
  • If the target column has since been deleted from the child list, the write is skipped rather than failing the save.

Existing rows are not restated. The value is written when a row is added or edited through the form, so a row nobody touches again keeps whatever was stored with it — changing the formula later does not rewrite history. The column badge reads (calculated — saved to Column) when a target is set.

5. Totals row

Any column — including calculated ones — can show an aggregated value in a footer row beneath the table: Count, Sum, Average, Min, or Max.

The total is computed across all currently displayed rows, including rows added locally but not yet saved — so it updates live as users add or remove line items. For a simple order total you no longer need Custom JS: a Sum total on the amount column does it natively.

A column’s prefix/suffix (§3) is applied to its total as well, so a summed amount column can read e.g. 1 250 kr while the value it sums stays a plain number.

6. Lookup columns

A repeating-section column can be a lookup into another list. Both variants are supported:

  • Single-select — the row references one related item and the column shows that item’s display value (its LookupField). Single-select lookups are inline-editable and can act as the base for lookup-derived columns (see §7).
  • Multi-value — the row can reference several related items; all selected display values are shown. Multi-value lookups are inline-editable but cannot be a base for a lookup-derived column.

The text shown is the target list’s configured LookupField (e.g. Title), just as on the main SharePoint form.

7. Lookup-derived columns

A lookup-derived column mirrors an additional field from the item chosen in a single-select lookup column. For example, selecting a Product lookup can also auto-populate that product’s Unit price as its own read-only column — without Unit price having to exist as a real field on the repeating section’s own list.

  • Auto-populated & read-only — the value comes from the selected item; it isn’t editable and isn’t written back to the child list (like a calculated column, it isn’t a real field on the list).
  • Usable in formulas — it’s just another [InternalName] token, so a calculated column such as [Quantity] * [UnitPrice] works with no new syntax.
  • Live — changing the lookup selection re-renders the row and recomputes the derived value and any calculated column that references it, automatically.
  • No extra query — the value is read from the same data that already loads the lookup’s dropdown options.

Requirements: the base column must be a single-select lookup (not multi-value) and must be editable in the section.

To configure: in the columns table, an editable single-select lookup column shows a small (+) icon next to its internal name (the same visual language as the calculated-column info icon). Click it to open a field picker scoped to the target lookup list’s fields, then choose which additional field to surface.

8. How it behaves for end users

  • On form open, the child rows are fetched and rendered as a table.
  • Users add a row (if allowed), filling in the inline-editable fields; edit existing rows in place; or delete rows.
  • Minimum / maximum constraints are enforced — e.g. the Add control is unavailable once the maximum is reached.
  • Newly added rows in a new form are held locally and saved once the parent item is created.

9. Custom JS callbacks

A global callback lets your Custom JS react to repeating-section data. It mirrors the existing window.dffs_vLookup_callback pattern.

9.1 When it fires

  • Once after the section’s rows are fetched from SharePoint on form open.
  • Again after every in-memory row change (add, edit, or delete).

So the callback always has the current state of the section, including unsaved local changes.

9.2 Parameters

ParameterDescription
idThe element id of the repeating section, in its prefixed form repeatingSection_{id} — exactly as shown at the bottom of the section’s configuration panel. Changed in 2.2: this previously arrived without the repeatingSection_ prefix.
itemsThe current array of rows. Each row contains the SharePoint field values plus Id and a _isNew: true flag on locally-added rows not yet saved.

9.3 Example — live total of a numeric column

window.dffs_repeatingSection_loaded_callback = function (id, items) {
  console.log("Repeating section", id, "has", items.length, "rows");

  // Sum a numeric field across all rows and write it to a header field:
  var total = items.reduce(function (sum, row) {
    return sum + (row.Amount || 0);
  }, 0);

  dffs_setFieldValue("TotalAmount", total);
};

9.4 Example — only react to a specific section

If you have more than one repeating section, branch on the id:

window.dffs_repeatingSection_loaded_callback = function (id, items) {
  if (id === "repeatingSection_a1b2c3d4-...") {   // the "Line items" section
    var unsaved = items.filter(function (r) { return r._isNew; }).length;
    dffs_setFieldValue("PendingLines", unsaved);
  }
};

Find the id in the form builder’s repeating-section configuration panel.

9.5 Reacting to individual row changes

dffs_repeatingSection_loaded_callback hands you the whole section after any change. Where you want the individual action instead, dffs_repeatingSection_callback(elementId, action, data) fires when a row is added, updated or removed, or the inline editor is cancelled — action is "add", "update", "remove" or "cancel", and data is { rowId, values }.

It reports intent, not persistence. Rows are staged and only written to the child list when the parent form is saved. A staged row is discarded if the form is cancelled, and rowId is negative for a row added but not yet saved. If you need to act once a row is genuinely written, act on the parent form’s save instead.

See the Custom JS API reference for both callbacks in full.

9.6 Notes

  • The callback is a single global function — define it once. If you need to handle multiple sections, branch on id inside the one function (as above).
  • Newly added rows carry _isNew: true until the parent form is saved; use this to distinguish queued rows from persisted ones.
  • Pair it with dffs_setFieldValue(...) to surface roll-ups (totals, counts) in ordinary form fields, or with any other Custom JS / DFFS function.

10. Worked example — order line items with a live total

Goal: On an Orders form, let users manage line items inline and show a running total.

  1. Create a child list OrderLines with fields Title, Quantity (Number), UnitPrice (Currency), Amount (Currency).
  2. On the Orders new/edit forms, add a Repeating Section bound to OrderLines.
    • Columns: Title, Quantity, UnitPrice, Amount
    • Allowed operations: Add, Edit, Delete
    • Minimum rows: 1
    • Heading: Line items
    • Empty-state message: No line items yet — click Add to create one.
  3. Turn on a totals row on the Amount column set to Sum — the table now shows a live order total in its footer, no code required.
  4. Optional — surface the total in a parent field. Add a read-only OrderTotal (Currency) field to the parent form.
  5. Then, in Custom JS, sum the Amount column whenever the section changes:
     window.dffs_repeatingSection_loaded_callback = function (id, items) {
       var total = items.reduce(function (sum, row) {
         return sum + (row.Amount || 0);
       }, 0);
       dffs_setFieldValue("OrderTotal", total);
     };

As users add or edit line items, the totals row updates live; the optional OrderTotal field mirrors it in the parent form.

Make it self-populating: add a Product single-select lookup to OrderLines and configure Unit price as a lookup-derived column from it (via the (+) icon), then define Amount as a calculated column [Quantity] * [UnitPrice] with a Sum totals row. Since a calculated Amount is display-only, derive the parent-field total from the row values in the callback if you still need it there.