# 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.

Uses `@svgrid/grid` (MIT). Category: Layout. Live demo: https://svgrid.com/demos/325-form-array/

Install: `npm i @svgrid/grid` (Svelte 5).

## 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

- [SvForm](https://svgrid.com/docs/help/ui-components/sv-form.md)

## Related articles

- [Reactivity with Large Arrays and Objects in Svelte 5](https://svgrid.com/blog/reactivity-large-arrays-objects/) - 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](https://svgrid.com/blog/avoiding-layout-thrash-custom-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](https://svgrid.com/blog/column-definitions-deep-dive/) - 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)

```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>
```

[View this example on GitHub](https://github.com/sv-grid/sv-grid/blob/main/examples/src/demos/325-form-array.svelte)
