Store repeating rows in a table field
What you'll see
You want a field that holds a table of rows — a repeating list where each row has several columns, such as a list of attributes, contacts, or line items — instead of a single value. You need to know how to set it up in the schema designer, and exactly what the data must look like inside the document when you fill or generate records programmatically.
What's actually happening
A table field is a repeating group with two sides that must stay in sync: the schema definition and the record data.
Schema side. In the schema document the field is an entry in Document.Fields[] with ObjectVariant: "Table". Unlike a normal field it has no Name property — instead it carries:
Array— the name of the property the rows are stored under in each record (e.g."Attributter"). This is how a table field is identified.Columns— an array of{ "Title": "…" }. The column Title becomes the property key on every row. There is no separate ASCII-key / label split like regular fields (Name vs Field); Title does both, so keep it a clean key (Attributt, not a sentence).Rows— a one-row scaffold with one emptyCells[]entry per column. EmptyCells[].Fields[]means a plain text column; typed columns (dropdown, number) are defined by placing a field definition inside a cell.
The designer also generates matching markup in schema.html: a <table class="table-grid"> with a <thead> of column titles and a <tbody data-array="…"> whose template cells carry data-func="input" data-name="<ColumnTitle>".
Record side. In each document, Document gets a property named exactly the schema’s Array value, holding an array of row objects. Every row has one key per column (the column Title) plus a generated ROWID. A sibling <Array>_type property is present alongside it.
What to do
1. Add the table field in the schema designer. Add a Table (grid) field, give it an array name, and define its columns. This is a one-time visual step — it writes both the Fields[] entry and the schema.html form for you, kept in sync.
2. Name columns as keys, not sentences. The column Title becomes the JSON key on every row, so use Attributt, Type — not “What the attribute is called”.
3. Fill data programmatically as an array of objects. Add a property to Document named exactly the array name. Each row object holds one key per column Title plus a ROWID (uuid-style). Include the sibling <name>_type.
// schema Fields[] entry (Table field)
{
"ObjectVariant": "Table",
"Array": "Attributter",
"Columns": [ { "Title": "Attributt" }, { "Title": "Type" } ],
"Columns_type": "Columns",
"Rows": [ { "Cells": [ { "Fields": [], "Fields_type": "Fields" },
{ "Fields": [], "Fields_type": "Fields" } ],
"Cells_type": "Cells" } ],
"Rows_type": "Rows"
}
// record Document — same key as Array
"Attributter": [
{ "Attributt": "E-postadresse", "Type": "Tekst", "ROWID": "102f422e-…" },
{ "Attributt": "Navn", "Type": "Tekst", "ROWID": "2bff8e62-…" }
],
"Attributter_type": "Table"4. Read the rows in a template. In a .hash display file, loop over the array and use the column titles as keys:
#for(let r of Attributter){#
<tr><td>#r.Attributt#</td><td>#r.Type#</td></tr>
#}#5. The schema definition (Fields[]) is the source of truth — schema.html is a generated artifact. The designer rebuilds schema.html server-side from Fields[] every time you edit the schema there, so it is overwritten regardless. If you change a schema by hand-editing the schema document directly, edit Fields[]; the matching schema.html is regenerated the next time the schema is opened in the designer, and any hand-edits you made to schema.html are discarded then. Until that regeneration the Admin form may still show the old columns, so prefer making structural changes in the designer — or reopen it there afterwards to refresh the form.