A surprising number of forms need to collect "some number of these." Invoice line items. Event attendees. Questionnaire entries where the user adds as many responses as they need. Checklist items on a task. Contacts attached to an account. The naive answer is "model each repeat as a related object and use a sub-form," and sometimes that's the right answer — when the repeated items have their own identity, their own permissions, their own history. But often they don't. The line items on this invoice are only meaningful as line items on this invoice. Making them a separate type is overkill. The replicator is the lighter-weight alternative: a repeating group of inputs stored directly on the parent record, managed inline, with no additional type in the data model.
A repeating group is exactly what it sounds like. A section of a form containing one or more input fields, which the user can add, remove, and reorder by drag. Each added instance is an independent copy of the section with its own values. Each removed instance goes through a delete confirmation so an accidental click doesn't wipe out work. The order is persistent — drag reorders the items, and the new order is saved with the form — which matters for things like invoice line items where the sequence is part of what the user cares about.
Per-item captions give each repeated group a meaningful label. "Line item 1," "Attendee 2," "Question 3" — or, if you prefer, a caption driven by an expression against the group's own fields, so the label on an attendee card becomes the attendee's name once they've filled it in. Small details like this keep long replicators navigable: a form with twelve line items is much more pleasant to scroll through when each one is labeled by its product name rather than by an index.
Initial state is configurable. A replicator can start empty — no items, a single "add" button inviting the user to begin — or with one preset item, or with several. For data imports, the initial state can be driven by existing data: when editing a record, the replicator loads with the items the record already carries. Add a new item and it appears at the end; reorder and the change is captured on save; remove and the removal is staged until the form is submitted.
Validation within groups uses the standard form rules — min, max, length, regex, required — applied per group rather than across the replicator as a whole. A line item missing its quantity highlights that item's quantity field with that item's error message; the other items remain unaffected. That per-group validation is what makes the replicator usable in forms with many items: errors are localized, and fixing one item doesn't disturb the others.
The replicator works equally well in the two places it's most needed. Object forms built with the regular form builder can include replicators tied to properties that store structured arrays. Canvas forms in the canvas page builder can include replicators too, with the same drag-to-sort and caption behavior. The consistency matters: implementers who learn the replicator on one surface use it the same way on the other.
Replicator item templates are where the canvas side gets more interesting. A canvas-driven template defines exactly how each repeated item looks — not just "a box with input fields," but any layout the canvas page builder supports. That's how forms with rich repeating content end up looking designed rather than generic: each item card can have headings, grouping, conditional fields, its own icon, its own color accents. The template is authored once in canvas; every item the replicator renders uses it.
Persistent data is a small but important detail: repeating items survive page reloads, modal navigations, and accidental tab-switches. If a user has been working on a twelve-item replicator and the browser closes, their work isn't lost the way it might be in a more naive implementation. That matters because replicators tend to appear on forms where the user has invested real effort — and losing that work is precisely the wrong moment for the platform to show its rough edges.
A practical guideline for when to use a replicator versus a related type: if the repeated items exist only as part of the parent record — they're not referenced elsewhere, they don't have their own permissions, they don't have their own audit trail, they don't need to be searched independently — the replicator is the right tool. If the repeated items are records in their own right — if a Line Item needs to be queryable across invoices, permissioned separately, linked from other places — they should be a related type, and the parent form should use a sub-form pattern. The question is ownership: does the sub-record have an independent life? If yes, model it as a type. If no, let the replicator handle it.
Worked example: invoice line items. The Invoice type has standard properties (customer, date, status) plus a line-items replicator. Each line-item group carries product, quantity, unit price, and a computed total. The user adds as many line items as the invoice needs, drags to reorder, and saves. The invoice's grand total is computed from the replicator's items through a display expression. No LineItem type in the data model; no cluttered reverse-lookup tab; no extra permission configuration. Just a form that matches the shape of an invoice.
Worked example: a canvas survey with repeating questions. The survey's canvas form has a replicator for entries, each entry using a custom item template that styles the question and its response area. Participants fill in as many responses as they want; the replicator keeps them organized visually; the handler on submit stores the collected responses in the right place. The whole thing is built from canvas blocks and the replicator — no scripts required.
For either surface, the replicator is one of those features that, once discovered, becomes part of every form an implementer builds. It's simple, it's direct, and it handles a whole class of patterns that used to require a separate type and a carefully-coordinated sub-form.