Repeater
Repeater: instantiates a template component once per item of an input array, inserting the instances as children at its position.
The Repeater consumes an array on items and creates one instance of its template component per item, placed into the visual tree where the Repeater sits (it must therefore be a child of a visual container). With templateType explicit (the default) the template is one component chosen on the template input; with templateType dynamic, a templateScript chooses a component per item at runtime. Item data flows into each instance through the instance's Component Inputs whose names match the item object's property names. The repeater diffs changes to the array, adding/removing instances rather than rebuilding everything.
When to use it
Any repeated UI: lists, grids, menus, table rows. Its idiomatic partner is Query Records for cloud data or a Static Data/Variable2 array for local data. Not for repeating pure logic without visuals — see For Each Actions.
At a glance
| Category | Visual |
| Type name | For Each |
| Available in | browser |
| SSR compatibility | safe |
| Provided by | noodl-viewer-react |
Inputs
Values
| Name | Type | Default | Description |
|---|---|---|---|
items | Array | — | The array or query result to repeat over; an empty value clears the list, including null and an empty array |
template | Component | — | Component to create once per item, with the item available to it as its Component Object |
templateScript | String | `// Set the 'component' variable to the name of the desired component for this item. | |
| // Component name must start with a '/'. | |||
| // A component in a sheet is referred to by '/#Sheet Name/Comopnent Name'. | |||
| // The data for each item is available in a variable called 'item' | |||
| component = '/MyComponent';` | JavaScript run per item that sets component to a component path; the item is available as item | ||
templateType | Enum (explicit, dynamic) | explicit | Explicit uses one component for every item; Dynamic picks one per item by running Script |
Signals
| Name | Type | Default | Description |
|---|---|---|---|
refresh | Signal | — | Rebuilds every item from the current Items, discarding any state the item components held |
Outputs
Values
| Name | Type | Default | Description |
|---|---|---|---|
itemActionItemId | String | — | Id of the item whose Repeater Item node last raised an action |
Signals
| Name | Type | Default | Description |
|---|---|---|---|
completed | Signal | — | Fires after every invocation, whatever the outcome — wire this to carry on regardless. Failure still fires and still carries its reason, so this cannot hide an error |
done | Signal | — | Fires once a Refresh has torn the list down and rebuilt it from the current Items, including when that leaves the list empty |
itemsRendered | Signal | — | Fires once every item component exists and has been added; item creation is spread across frames, so this is the only honest moment to measure or scroll the list |
Failure outputs
| Name | Type | Default | Description |
|---|---|---|---|
failure | Signal | — | Fires when the Repeater could not rebuild: no Items bound, no Template set, or nothing to render into |
Dynamic ports
This node's port list changes at runtime (declared-port-groups, runtime-discovered); the tables above may be incomplete for a given instance.
The "Template Type"/template component determines dynamic input ports: inputs of the item template component are exposed so static values can be fed to each created item.
| Condition | Inputs shown | Outputs shown |
|---|---|---|
| templateType = explicit OR templateType NOT SET | template | — |
| templateType = dynamic | templateScript | — |
Ports at runtime
Two dynamic mechanisms: template/templateScript swap in and out based on templateType (declared-port-groups), and outputs named after signals sent by item components are registered on demand (runtime-discovered) — an item component's Component Outputs signal appears as an output port here so a list can bubble events up.
Patterns
- Query Records
items→ Repeateritems: the canonical data-driven list. - Item component declares Component Inputs matching record property names — no explicit per-field wiring at the list level.
Watch out for
- Triggering
refreshwhenever data changes: the repeater already diffsitems; refresh forces a full rebuild and loses instance state. - Binding huge unpaged query results into a repeater; page or limit the query instead.
Examples
List page: Repeater fed by Query Records
The canonical data-list shape. Query Records (DbCollection2) fetches a database class and exposes the result on its items array output; the Repeater (For Each) consumes that array and instantiates its template component once per record. Each record's properties are delivered to the item component through Component Inputs whose names match the record's property names — the item component reads them like any other input. The Repeater and its item template component are separate components by design.
Card grid: Query Records into a Repeater, in a Columns node that reflows on its own
The canonical data grid, and the layout decision most often got wrong. Use a Columns node with sizing "autoFit" and a minWidth of 260-320px: it fits as many columns as the CONTAINER holds and reflows by itself, with no breakpoints to maintain. Columns handles a Repeater child correctly — the Repeater draws nothing and adds its items as siblings, so Columns skips it and gives each real item a column box. The card component is width 100% and lets the column size it, which is what makes the SAME card work in this grid, in a 2-up related row, and in a sidebar. Do NOT reach for a Group with flexWrap: a wrapped flex row does not shrink its children, so each item needs a hardcoded percentage track, and — the part that matters — no Group anywhere in the runtime has a breakpoint, so that layout can never collapse on a narrow screen. Record fields reach the item through Component Inputs whose names match the record properties, and wiring a field straight into a Group's visible port is conditional rendering with no logic node.
A rich-text editor with a toolbar and local drafts
The larger companion to the single-button component: a Static Data node holds the list of toolbar commands, a For Each draws one button per entry, and the editor itself is mounted by a JavaScriptFunction onto a Group's element. Two details are worth more than the editor. The draft is saved to localStorage on a Timer and read back on mount, which is the whole of 'don't lose my work' and costs two function nodes. And Model2 plus SetModelProperties keep the document in the project's own data model rather than only inside the third-party editor, so something other than the editor can read what was typed. ⚠️ The toolbar is driven by data, so adding a command is a row in the Static Data node — not a new button.
A wrapping row of content-width items, with no CSS
Items that keep their own width and flow onto the next line when they run out of room — the layout a chip row, a filter bar or a tag list wants, and one people reach for a CSS Definition node to get. It needs no CSS: the container is a Group with Multi Line Wrap on, Layout set to row, a Column Gap and a Row Gap (the two gap ports only appear once wrapping is on) and Align Y at top, and each item is a Group at Content Size. ⚠️ That last part is the half that is usually missed. An item at Content Size gets no width of its own, and the runtime gives every node flex-shrink: 0 and only grants flex-grow to a percentage width — so Content Size already is flex: 0 0 auto, and an item left at 100% instead becomes flex-grow: 100 and swallows the whole line. 🔴 This is not masonry: rows are laid out independently and nothing balances columns or packs items upward by height.
Related nodes
Query Records, Component Inputs, Component Outputs, Repeater Item, Static Array
This page is generated from node-catalog-enriched.json. Do not edit it by hand — run npm run docs:nodes to regenerate, and fix the source enrichment instead.