Repeating Sections
Professional & EnterpriseA 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)
- In the form builder, click + and add the Repeating Section element to a column.
- Open its configuration panel and set:
| Setting | What it controls |
|---|---|
| Child list | The 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 field | The 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 operations | Which of Add / Edit / Delete end users may perform. |
| Row constraints | Minimum and maximum number of rows allowed. |
| Heading | A custom heading shown above the table (supports MUI multi-language overrides). |
| Empty-state message | Text shown when the section has no rows yet (supports MUI). |
| Column display title | Optional 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 / suffix | Optional 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 values | Field/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. |
- 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
elementIdargument 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:
CONCATENATEand 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
Amountcalculated 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. ACONCATENATE-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
| Parameter | Description |
|---|---|
id | The 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. |
items | The 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
idin 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
rowIdis 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
idinside the one function (as above). - Newly added rows carry
_isNew: trueuntil 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.
- Create a child list OrderLines with fields
Title,Quantity(Number),UnitPrice(Currency),Amount(Currency). - On the Orders
new/editforms, 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.
- Columns:
- Turn on a totals row on the
Amountcolumn set to Sum — the table now shows a live order total in its footer, no code required. - Optional — surface the total in a parent field. Add a read-only
OrderTotal(Currency) field to the parent form. - Then, in Custom JS, sum the
Amountcolumn 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
Amountas a calculated column[Quantity] * [UnitPrice]with aSumtotals row. Since a calculatedAmountis display-only, derive the parent-field total from the row values in the callback if you still need it there.