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 values in a form, including sub-object values inside of an array. This is useful for managing dynamic lists like multiple addresses, contacts, or any repeating data structure.
Basic Usage
To use an array field, set mode="array" on the form.Field component:
function App() {
const form = useForm({
defaultValues: {
people: [],
},
})
return (
<form.Field name="people" mode="array">
{(field) => (
<div>
{field.state.value.map((_, i) => {
// Render each item
})}
</div>
)}
</form.Field>
)
}
Adding Items
Use the pushValue method to add items to the array:
<button onClick={() => field.pushValue({ name: '', age: 0 })} type="button">
Add person
</button>
Accessing Sub-fields
Create nested fields using bracket notation:
<form.Field key={i} name={`people[${i}].name`}>
{(subField) => (
<input
value={subField.state.value}
onChange={(e) => subField.handleChange(e.target.value)}
/>
)}
</form.Field>
Complete Example
Here’s a full example with add and remove functionality:
function App() {
const form = useForm({
defaultValues: {
people: [],
},
onSubmit({ value }) {
alert(JSON.stringify(value))
},
})
return (
<div>
<form
onSubmit={(e) => {
e.preventDefault()
e.stopPropagation()
form.handleSubmit()
}}
>
<form.Field name="people" mode="array">
{(field) => {
return (
<div>
<h3>People</h3>
{field.state.value.length === 0 ? (
<p>No people added yet.</p>
) : (
field.state.value.map((_, i) => (
<div key={i}>
<form.Field name={`people[${i}].name`}>
{(subField) => (
<div>
<label>
<div>Name for person {i + 1}</div>
<input
value={subField.state.value}
onChange={(e) =>
subField.handleChange(e.target.value)
}
/>
</label>
<button
type="button"
onClick={() => field.removeValue(i)}
>
Remove Person
</button>
</div>
)}
</form.Field>
</div>
))
)}
<button
onClick={() => field.pushValue({ name: '', age: 0 })}
type="button"
>
Add person
</button>
</div>
)
}}
</form.Field>
<form.Subscribe
selector={(state) => [state.canSubmit, state.isSubmitting]}
children={([canSubmit, isSubmitting]) => (
<button type="submit" disabled={!canSubmit}>
{isSubmitting ? '...' : 'Submit'}
</button>
)}
/>
</form>
</div>
)
}
Array Field Methods
TanStack Form provides several methods to manipulate array fields:
pushValue
Add an item to the end of the array:
field.pushValue({ name: '', age: 0 })
removeValue
Remove an item at a specific index:
insertValue
Insert an item at a specific position:
field.insertValue(index, { name: '', age: 0 })
replaceValue
Replace an item at a specific index:
field.replaceValue(index, { name: 'John', age: 30 })
swapValues
Swap two items by their indices:
field.swapValues(indexA, indexB)
moveValue
Move an item from one index to another:
field.moveValue(fromIndex, toIndex)
clearValues
Remove all items from the array:
Validation with Array Fields
You can validate individual array items or the entire array:
Validating Individual Items
<form.Field
name={`people[${i}].age`}
validators={{
onChange: ({ value }) =>
value < 18 ? 'Must be 18 or older' : undefined,
}}
>
{(field) => (
<>
<input
type="number"
value={field.state.value}
onChange={(e) => field.handleChange(e.target.valueAsNumber)}
/>
{!field.state.meta.isValid && (
<em>{field.state.meta.errors.join(', ')}</em>
)}
</>
)}
</form.Field>
Validating the Entire Array
<form.Field
name="people"
mode="array"
validators={{
onChange: ({ value }) =>
value.length === 0 ? 'At least one person is required' : undefined,
}}
>
{(field) => (
<div>
{!field.state.meta.isValid && (
<em>{field.state.meta.errors.join(', ')}</em>
)}
{/* Array content */}
</div>
)}
</form.Field>
Complex Nested Structures
You can nest arrays and objects to create complex data structures:
const form = useForm({
defaultValues: {
teams: [
{
name: '',
members: [
{
firstName: '',
lastName: '',
},
],
},
],
},
})
return (
<form.Field name="teams" mode="array">
{(teamsField) => (
<div>
{teamsField.state.value.map((_, teamIndex) => (
<div key={teamIndex}>
<form.Field name={`teams[${teamIndex}].name`}>
{(field) => (
<input
value={field.state.value}
onChange={(e) => field.handleChange(e.target.value)}
/>
)}
</form.Field>
<form.Field name={`teams[${teamIndex}].members`} mode="array">
{(membersField) => (
<div>
{membersField.state.value.map((_, memberIndex) => (
<div key={memberIndex}>
<form.Field
name={`teams[${teamIndex}].members[${memberIndex}].firstName`}
>
{(field) => (
<input
value={field.state.value}
onChange={(e) => field.handleChange(e.target.value)}
/>
)}
</form.Field>
</div>
))}
<button
type="button"
onClick={() =>
membersField.pushValue({ firstName: '', lastName: '' })
}
>
Add Member
</button>
</div>
)}
</form.Field>
</div>
))}
<button
type="button"
onClick={() =>
teamsField.pushValue({ name: '', members: [] })
}
>
Add Team
</button>
</div>
)}
</form.Field>
)
Best Practices
Always use keys
When mapping over array fields, always provide a unique key prop:{field.state.value.map((item, i) => (
<div key={i}>
{/* Use index as key only if items don't have unique IDs */}
</div>
))}
Initialize with empty arrays
Always initialize array fields with an empty array in defaultValues:const form = useForm({
defaultValues: {
people: [], // Not undefined
},
})
Validate before submission
Add validation to ensure array fields meet requirements:validators={{
onSubmit: ({ value }) =>
value.people.length === 0 ? 'Add at least one person' : undefined,
}}