Skip to content
vue
<script setup lang="ts">
import { useForm } from "@tanstack/vue-form";
import { ref } from "vue";

interface Person {
	name: string;
	age: number;
}

const MIN_ITEMS = 2;
const MAX_ITEMS = 10;
const submittedValue = ref<unknown>(null);

const form = useForm({
	defaultValues: {
		people: Array.from(
			{ length: MIN_ITEMS },
			() => (
				{
					name: "Math",
					age: 16,
				} satisfies Person
			),
		),
	},
	onSubmit: ({ value }) => {
		submittedValue.value = value;
	},
	validators: {
		onSubmit: ({ value }) => {
			if (value.people.length < MIN_ITEMS) {
				return `At least ${MIN_ITEMS} items are required.`;
			}
			if (value.people.length > MAX_ITEMS) {
				return `At most ${MAX_ITEMS} items are allowed.`;
			}
			return;
		},
	},
});
const people = form.useStore((state) => state.values.people);

function addPerson() {
	if (people.value.length >= MAX_ITEMS) {
		return;
	}
	void form.pushFieldValue("people", {
		name: "Math",
		age: 16,
	});
}

function removePerson(index: number) {
	if (people.value.length <= MIN_ITEMS) {
		return;
	}
	void form.removeFieldValue("people", index);
}
</script>

<template>
	<form @submit.prevent="void form.handleSubmit()">
		<div
			v-for="(_, index) in people"
			:key="index"
		>
			<form.Field
				:name="`people[${index}].name`"
				v-slot="{ field, state }"
			>
				<input
					type="text"
					:value="state.value"
					@input="field.handleChange(($event.target as HTMLInputElement).value)"
				/>
			</form.Field>

			<form.Field
				:name="`people[${index}].age`"
				:validators="{
					onChange: ({ value }) => value < 18 ? 'Too young !' : undefined,
				}"
				v-slot="{ field, state }"
			>
				<input
					type="number"
					:value="state.value"
					@input="field.handleChange(Number(($event.target as HTMLInputElement).value))"
				/>

				<p v-if="state.meta.errors[0]">
					{{ state.meta.errors[0] }}
				</p>
			</form.Field>

			<button
				type="button"
				:disabled="people.length <= MIN_ITEMS"
				@click="removePerson(index)"
			>
				Remove this item
			</button>
		</div>

		<button
			type="button"
			:disabled="people.length >= MAX_ITEMS"
			@click="addPerson"
		>
			Add another item
		</button>

		<button type="submit">
			Submit
		</button>

		<pre v-if="submittedValue">{{ submittedValue }}</pre>
	</form>
</template>

Released under the MIT License.