Form groups in @tanstack/vue-form
Official guide: https://tanstack.com/form/latest/docs/framework/vue/guides/form-groups
A form group is a sub-form over one key of the form data, for example one step of a stepper. It validates and submits independently of the parent form.
Usage
<script setup lang="ts">
import { ref } from 'vue'
import { useForm } from '@tanstack/vue-form'
const step = ref(0)
const form = useForm({
defaultValues: {
step1: { name: '' },
step2: { age: 0 },
},
})
</script>
<template>
<form.FormGroup v-if="step === 0" name="step1" v-slot="{ group: formGroup }">
<form @submit.prevent.stop="formGroup.handleSubmit()">
<form.Field name="step1.name">
<template v-slot="{ field }">
<input
:value="field.state.value"
@input="(e) => field.handleChange((e.target as HTMLInputElement).value)"
/>
</template>
</form.Field>
</form>
</form.FormGroup>
</template>- The slot receives
{ group, state }(src/useFormGroup.tsx:525-529, prepared source).groupis aFormGroupApiwith form-like methods such ashandleSubmit,deleteField, andinsertFieldValue. formGroup.handleSubmit()validates and submits only the group. Callform.handleSubmit()to submit the whole form, for example from the last step.- Group callbacks:
onGroupSubmit,onGroupSubmitInvalid, andonSubmitMetaprops onform.FormGroup. - The
useFormGroup(opts)hook returns{ api, state }withstateas aReadonly<Ref>(src/useFormGroup.tsx:296-316); the component form is the normal path in templates.
Group validation
Group validators can return errors three ways:
<form.FormGroup
name="step1"
:validators="{
onChange: ({ value, groupApi }) => ({
group: value.name === 'error' ? 'Group error' : undefined,
fields: {
name: value.name === 'error' ? 'Field error' : undefined,
},
}),
}"
v-slot="{ group: formGroup }"
>
{{ formGroup.state.meta.errorMap.onChange }}
</form.FormGroup>groupis the group-level error, read fromformGroup.state.meta.errorsorerrorMap.fieldskeys are relative to the group (name, notstep1.name), matching how Standard Schemas address group fields.- Standard Schemas are accepted directly, and compose with the parent form schema: pass
step1Schemato the group andz.object({ step1: step1Schema, step2: step2Schema })touseForm. Partially validated data still flags errors if a group is bypassed.
Dynamic validation
With onDynamic and revalidateLogic(), pass the sub-schema to the group, not to useForm. A form-level onDynamic only re-runs its onChange when the form itself is submitted; the group's onDynamic keys off formGroup.submissionAttempts (official guide).
<form.FormGroup name="step1" :validators="{ onDynamic: step1Schema }" />Group state meta
formGroup.state.meta.isFieldsValid: field-level validators have no errorsformGroup.state.meta.isGroupValid: group-level validators have no errorsformGroup.state.meta.isValid: both are validformGroup.state.meta.isSubmitting: the group submit is in flight