Documentation Index
Fetch the complete documentation index at: https://mintlify.com/tanstack/form/llms.txt
Use this file to discover all available pages before exploring further.
TanStack Form supports arrays as form values, enabling you to build dynamic forms with lists of items.
Basic Array Usage
To work with arrays, use field.state.value on an array field with Svelte’s each blocks.
Simple Array Example
<script>
import { createForm } from '@tanstack/svelte-form'
const form = createForm(() => ({
defaultValues: {
people: [],
},
}))
</script>
<form.Field name="people" mode="array">
{#snippet children(field)}
{#each field.state.value as person, i}
<!-- Render each person -->
{/each}
{/snippet}
</form.Field>
Note the mode="array" prop, which tells the field to operate in array mode.
Adding Items to Arrays
Use the pushValue method to add new items to an array field:
<form.Field name="people" mode="array">
{#snippet children(field)}
<button onclick={() => field.pushValue({ name: '', age: 0 })} type="button">
Add person
</button>
{/snippet}
</form.Field>
The list automatically regenerates every time you call pushValue.
Accessing Nested Fields
Access individual array items using bracket notation in the field name:
<form.Field name={`people[${i}].name`}>
{#snippet children(subField)}
<input
value={subField.state.value}
oninput={(e) => {
subField.handleChange(e.currentTarget.value)
}}
/>
{/snippet}
</form.Field>
Complete Array Example
Here’s a full example with adding items and nested field access:
<script lang="ts">
import { createForm } from '@tanstack/svelte-form'
const form = createForm(() => ({
defaultValues: {
people: [] as Array<{ age: number; name: string }>,
},
onSubmit: ({ value }) => alert(JSON.stringify(value)),
}))
</script>
<form
id="form"
onsubmit={(e) => {
e.preventDefault()
e.stopPropagation()
form.handleSubmit()
}}
>
<form.Field name="people">
{#snippet children(field)}
<div>
{#each field.state.value as person, i}
<form.Field name={`people[${i}].name`}>
{#snippet children(subField)}
<div>
<label>
<div>Name for person {i}</div>
<input
value={person.name}
oninput={(e: Event) => {
const target = e.target as HTMLInputElement
subField.handleChange(target.value)
}}
/>
</label>
</div>
{/snippet}
</form.Field>
{/each}
<button
onclick={() => field.pushValue({ name: '', age: 0 })}
type="button"
>
Add person
</button>
</div>
{/snippet}
</form.Field>
<button type="submit"> Submit </button>
</form>
Array Field Methods
The field API provides several methods for managing arrays:
pushValue(item)
Add an item to the end of the array:
field.pushValue({ name: '', age: 0 })
removeValue(index)
Remove an item at a specific index:
field.removeValue(2) // Remove the third item
insertValue(index, item)
Insert an item at a specific position:
field.insertValue(1, { name: 'John', age: 30 })
replaceValue(index, item)
Replace an item at a specific index:
field.replaceValue(0, { name: 'Jane', age: 25 })
swapValues(indexA, indexB)
Swap two items in the array:
field.swapValues(0, 2) // Swap first and third items
moveValue(from, to)
Move an item from one index to another:
field.moveValue(0, 3) // Move first item to fourth position
clearValues()
Remove all items from the array:
Add remove buttons to let users delete individual items:
<form.Field name="people">
{#snippet children(field)}
<div>
{#each field.state.value as person, i}
<div>
<form.Field name={`people[${i}].name`}>
{#snippet children(subField)}
<input
value={person.name}
oninput={(e: Event) => {
const target = e.target as HTMLInputElement
subField.handleChange(target.value)
}}
/>
{/snippet}
</form.Field>
<button
type="button"
onclick={() => field.removeValue(i)}
>
Remove
</button>
</div>
{/each}
</div>
{/snippet}
</form.Field>
Validating Array Fields
You can validate both the array itself and individual array items:
Array-Level Validation
Validate the entire array:
<form.Field
name="people"
validators={{
onChange: ({ value }) =>
value.length < 1 ? 'At least one person is required' : undefined,
}}
>
{#snippet children(field)}
<!-- ... -->
{#if field.state.meta.errors}
<em>{field.state.meta.errors.join(', ')}</em>
{/if}
{/snippet}
</form.Field>
Item-Level Validation
Validate individual array items:
<form.Field
name={`people[${i}].name`}
validators={{
onChange: ({ value }) =>
value.length < 2 ? 'Name must be at least 2 characters' : undefined,
}}
>
{#snippet children(field)}
<input
value={field.state.value}
oninput={(e: Event) => {
const target = e.target as HTMLInputElement
field.handleChange(target.value)
}}
/>
{#if field.state.meta.errors}
<em>{field.state.meta.errors[0]}</em>
{/if}
{/snippet}
</form.Field>
Complex Nested Objects
Arrays can contain complex objects with multiple fields:
<script lang="ts">
interface Person {
firstName: string
lastName: string
age: number
email: string
}
const form = createForm(() => ({
defaultValues: {
people: [] as Array<Person>,
},
}))
</script>
<form.Field name="people">
{#snippet children(field)}
{#each field.state.value as person, i}
<div>
<form.Field name={`people[${i}].firstName`}>
{#snippet children(subField)}
<input value={subField.state.value} oninput={(e) => subField.handleChange(e.target.value)} />
{/snippet}
</form.Field>
<form.Field name={`people[${i}].lastName`}>
{#snippet children(subField)}
<input value={subField.state.value} oninput={(e) => subField.handleChange(e.target.value)} />
{/snippet}
</form.Field>
<form.Field name={`people[${i}].age`}>
{#snippet children(subField)}
<input type="number" value={subField.state.value} oninput={(e) => subField.handleChange(e.target.valueAsNumber)} />
{/snippet}
</form.Field>
<form.Field name={`people[${i}].email`}>
{#snippet children(subField)}
<input type="email" value={subField.state.value} oninput={(e) => subField.handleChange(e.target.value)} />
{/snippet}
</form.Field>
</div>
{/each}
{/snippet}
</form.Field>
Empty State
Handle the case when the array is empty using Svelte’s else block:
<form.Field name="people">
{#snippet children(field)}
<div>
{#each field.state.value as person, i}
<!-- Render person -->
{:else}
<p>No people added yet. Click "Add person" to get started.</p>
{/each}
<button onclick={() => field.pushValue({ name: '', age: 0 })} type="button">
Add person
</button>
</div>
{/snippet}
</form.Field>
Array Keying
For better performance with reordering, use Svelte’s keyed each blocks:
<script lang="ts">
interface Person {
id: string
name: string
}
const form = createForm(() => ({
defaultValues: {
people: [] as Array<Person>,
},
}))
</script>
<form.Field name="people">
{#snippet children(field)}
{#each field.state.value as person, i (person.id)}
<form.Field name={`people[${i}].name`}>
{#snippet children(subField)}
<input value={subField.state.value} oninput={(e) => subField.handleChange(e.target.value)} />
{/snippet}
</form.Field>
{/each}
{/snippet}
</form.Field>
Next Steps