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:
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:
<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:
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)
}}"
/>
`
},
)}
`
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;
}
}
Your weekly dose of JavaScript news. Delivered every Monday to over 100,000 devs, for free.