File: arrays.md | Updated: 11/15/2025
Search...
+ K
Auto
Docs Examples GitHub Contributors
Docs Examples GitHub Contributors
Docs Examples GitHub Contributors
Docs Examples Github Contributors
Docs Examples Github Contributors
Docs Examples Github Contributors
Docs Examples Github Contributors
Docs Examples Github Contributors
Maintainers Partners Support Learn StatsBETA Discord Merch Blog GitHub Ethos Brand Guide
Documentation
Framework
Lit
Version
v1
Search...
+ K
Menu
Getting Started
Guides
API Reference
Examples
Framework
Lit
Version
v1
Menu
Getting Started
Guides
API Reference
Examples
On this page
Copy Markdown
TanStack Form supports arrays as values in a form, including sub-object values inside of an array.
To use an array, you can use field.state.value on an array value, as in:
ts
export class TestForm extends LitElement {
#form = new TanStackFormController(this, {
defaultValues: {
people: [] as Array<{ name: string; age: string }>,
},
})
render() {
return html`
<form
id="form"
@submit=${(e: Event) => {
e.preventDefault()
}}
>
<h1>Please enter your details</h1>
${this.#form.field(
{
name: `people`,
},
(peopleField) => {
return html`${repeat(
peopleField.state.value,
(_, index) => index,
(_, index) => {
return html` // ... `
},
)} `
},
)}
</form>
`
}
}
export class TestForm extends LitElement {
#form = new TanStackFormController(this, {
defaultValues: {
people: [] as Array<{ name: string; age: string }>,
},
})
render() {
return html`
<form
id="form"
@submit=${(e: Event) => {
e.preventDefault()
}}
>
<h1>Please enter your details</h1>
${this.#form.field(
{
name: `people`,
},
(peopleField) => {
return html`${repeat(
peopleField.state.value,
(_, index) => index,
(_, index) => {
return html` // ... `
},
)} `
},
)}
</form>
`
}
}
This will generate the mapped HTML every time you run pushValue on the field:
html
<div class="container">
<button type="button" @click="${()" ="">
{ peopleField.pushValue({name: "",age: ""}) }}> Add Person
</button>
</div>
<div class="container">
<button type="button" @click="${()" ="">
{ peopleField.pushValue({name: "",age: ""}) }}> Add Person
</button>
</div>
Finally, you can use a subfield like so:
ts
return html`
${this.#form.field(
{
name: `people[${index}].name`,
},
(field) => {
return html`
<input
type="text"
placeholder="Name"
.value="${field.state.value}"
@input="${(e: Event) => {
const target = e.target as HTMLInputElement
field.handleChange(target.value)
}}"
/>
`
},
)}
`
return html`
${this.#form.field(
{
name: `people[${index}].name`,
},
(field) => {
return html`
<input
type="text"
placeholder="Name"
.value="${field.state.value}"
@input="${(e: Event) => {
const target = e.target as HTMLInputElement
field.handleChange(target.value)
}}"
/>
`
},
)}
`
typescript
export class TestForm extends LitElement {
#form = new TanStackFormController(this, {
defaultValues: {
people: [] as Array<{ name: string}>,
},
});
render() {
return html`
<form
id="form"
@submit=${(e: Event) => {
e.preventDefault();
}}
>
<h1>Please enter your details</h1>
${this.#form.field(
{
name: `people`,
},
(peopleField) => {
return html`${repeat(
peopleField.state.value,
(_, index) => index,
(_, index) => {
return html`
${this.#form.field(
{
name: `people[${index}].name`,
},
(field) => {
return html` <div>
<div class="container">
<label>Name</label>
<input
type="text"
placeholder="Name"
.value="${field.state.value}"
@input="${(e: Event) => {
const target = e.target as HTMLInputElement;
field.handleChange(target.value);
}}"
/>
</div>
</div>`;
}
)}
`;
}
)}
<div class="container">
<button
type="button"
@click=${() => {
peopleField.pushValue({
name: "",
});
}}
>
Add Person
</button>
</div> `;
}
)}
<div class="container">
<button type="submit" ?disabled=${this.#form.api.state.isSubmitting}>
${this.#form.api.state.isSubmitting ? html` Submitting` : "Submit"}
</button>
<button
type="button"
id="reset"
@click=${() => {
this.#form.api.reset();
}}
>
Reset
</button>
</div>
</form>
`;
}
declare global {
interface HTMLElementTagNameMap {
"test-form": TestForm;
}
}
export class TestForm extends LitElement {
#form = new TanStackFormController(this, {
defaultValues: {
people: [] as Array<{ name: string}>,
},
});
render() {
return html`
<form
id="form"
@submit=${(e: Event) => {
e.preventDefault();
}}
>
<h1>Please enter your details</h1>
${this.#form.field(
{
name: `people`,
},
(peopleField) => {
return html`${repeat(
peopleField.state.value,
(_, index) => index,
(_, index) => {
return html`
${this.#form.field(
{
name: `people[${index}].name`,
},
(field) => {
return html` <div>
<div class="container">
<label>Name</label>
<input
type="text"
placeholder="Name"
.value="${field.state.value}"
@input="${(e: Event) => {
const target = e.target as HTMLInputElement;
field.handleChange(target.value);
}}"
/>
</div>
</div>`;
}
)}
`;
}
)}
<div class="container">
<button
type="button"
@click=${() => {
peopleField.pushValue({
name: "",
});
}}
>
Add Person
</button>
</div> `;
}
)}
<div class="container">
<button type="submit" ?disabled=${this.#form.api.state.isSubmitting}>
${this.#form.api.state.isSubmitting ? html` Submitting` : "Submit"}
</button>
<button
type="button"
id="reset"
@click=${() => {
this.#form.api.reset();
}}
>
Reset
</button>
</div>
</form>
`;
}
declare global {
interface HTMLElementTagNameMap {
"test-form": TestForm;
}
}
