Field array form
SvForm field array (type: array): a repeatable group with add / remove rows, per-row validation (each item validates against itself), required / minItems gating, and an array value in the submitted payload - an invoice line-items form.
A live, editable Svelte 5 component example from the SvGrid gallery (Layout). Read the SvGrid UI component docs for the full API.
About this example
A field array in SvForm for Svelte 5: type 'array' with itemFields renders a repeatable group with add and remove rows, each row validating against its own values so within-row rules work, required and minItems gating the count, and an array of item objects in the submitted payload. The demo is an invoice line-items form.
SvForm FIELD ARRAY: a repeatable group. type: 'array' + itemFields renders add/remove rows; each row's fields validate against that row (so within-row rules work), and required / minItems gate the count. The value is an array of item objects, included in the submitted payload.
Imports, features and API used
Imports: @svgrid/grid
Frequently asked questions
How do I declare a repeatable group?
Add a field with type: 'array' and itemFields listing the fields of one row. The form renders the rows from the initial array with add and remove buttons.
How does per-row validation work?
Each row's validate functions receive that row's values, so a rule such as quantity greater than zero applies within the row, and errors show on the row's field.
How do I require at least one row?
Set minItems on the array field, or required for at least one; the form blocks submit and shows a message until the count is met.
Related documentation
Related articles
- Reactivity with Large Arrays and Objects in Svelte 5 - Deep proxies are powerful but not free. Here is when to pay the cost and when to use $state.raw to keep bulk data loads fast in a SvGrid app.
- Avoiding Layout Thrash in Custom Grid Cells - Layout thrash from interleaved DOM reads and writes is the most common cause of scroll jank in grids with custom cells - here is how to find it and design your way out of it.
- Column Definitions in SvGrid - Fields, Accessors, and Formatting - A practical look at how ColumnDef controls field binding, value resolution, sort behavior, and custom cell rendering in SvGrid.
Source code (325-form-array.svelte)
<script lang="ts">
/**
* SvForm FIELD ARRAY: a repeatable group. `type: 'array'` + `itemFields` renders
* add/remove rows; each row's fields validate against that row (so within-row
* rules work), and `required` / `minItems` gate the count. The value is an array
* of item objects, included in the submitted payload.
*/
import { SvForm, rules } from '@svgrid/grid'
import type { FormEntry } from '@svgrid/grid'
const schema: FormEntry[] = [
{ name: 'client', label: 'Bill to', required: true, full: true },
{ name: 'items', label: 'Line items', type: 'array', required: true, minItems: 1, addLabel: '+ Add line', itemFields: [
{ name: 'desc', label: 'Description', required: true },
{ name: 'qty', label: 'Qty', type: 'number', required: true, rules: [rules.min(1)] },
{ name: 'price', label: 'Unit price', type: 'number', required: true, rules: [rules.min(0)] },
] },
]
let result = $state<string>('')
</script>
<div class="wrap">
<header>
<h2>Field array</h2>
<p>
A repeatable group: add and remove line items, each validated per row (try
submitting with an empty description or qty 0). The value is an array of item
objects - submit to see the payload.
</p>
</header>
<div class="card">
<SvForm
fields={schema}
submitLabel="Create invoice"
initial={{ client: '', items: [{ desc: '', qty: 1, price: 0 }] }}
onSubmit={(v) => { result = JSON.stringify(v, null, 2) }}
/>
</div>
{#if result}
<pre class="result">{result}</pre>
{/if}
</div>
<style>
.wrap { padding: 20px; max-width: 680px; display: flex; flex-direction: column; gap: 16px; }
header h2 { margin: 0 0 4px; font-size: 20px; font-weight: 700; }
header p { margin: 0; color: var(--sg-muted, #64748b); font-size: 13.5px; line-height: 1.5; }
.card { padding: 20px; border: 1px solid var(--sg-border, #e2e8f0); border-radius: 12px; }
.result { margin: 0; padding: 14px; background: var(--sg-header-bg, #f8fafc); border: 1px solid var(--sg-border, #e2e8f0); border-radius: 10px; font-size: 12px; overflow: auto; }
</style>More Layout examples
- Account & security settings console - A real SaaS settings surface composed from the UI kit: SvMenubar app bar, promise + Undo-action toasts on save, SvPopconfirm on destructive rows, SvHoverCard teammate previews, and frame input adornments (leading icons, prefix affixes, masked API key). Profile / Team / Security tabs over SvCard + SvStat.
- Invoice builder - An adornment-heavy money form: currency prefixes, % suffixes, masked tax IDs (frame adornments), line items with SvPopconfirm delete + Undo action toasts, live SvStat totals, an SvHoverCard tax hint, and a promise toast on Send. Pure UI-kit composition.
- Operations KPI dashboard - A KPI console: SvStat + SvSparkline tiles, an SvGauge SLA dial, SvHoverCard drill-down previews per service, an SvMenubar toolbar (View / Range / Actions) and a promise toast on Refresh. Pure UI-kit composition, no grid dependency.
- Form: rich field types - SvForm reaching the whole input suite from one schema - phone, country, mask, combobox, radio, slider, tags, datetime and file - plus per-field help text, a readonly field, column span, and a promise toast on submit.
- Form: cascading fields - SvForm dependent selects - a child list derives from the parent value via a function `options`, and `dependsOn` clears the child when the parent changes so a stale selection never lingers. Country -> State -> City.