Describe the bug
With index keys (as in examples/vue/array), after array.removeValue(0) the form creates a new field API instance for issues[0].title, but the mounted <form.Field :name="issues[0].title"> keeps the old instance. The form meta for issues[0].title contains the error that shifted from the removed row's neighbour, but the slot never renders it.
This happens with markup rendered directly inside the <form.Field> slot. It's unrelated to createFormHook.
Diagnosis (uses the internal _tryGetFieldApi only to compare instances):
|
Result |
Before removal: slot field === form._tryGetFieldApi('issues[0].title') |
true |
| After removal: internal instance replaced |
true |
After removal: slot field === internal instance |
false |
After removal: form.getFieldMeta('issues[0].title').errors |
["Required"] |
| After removal: rendered errors |
none |
useField (src/VueForm/useField.lib.ts) only recreates the field API when form, name or resetVersion changes. After removeValue, the name stays the same, so the component isn't moved to the new instance.
Your minimal, reproducible example
<script setup lang="ts">
import { useForm } from '@tanstack/vue-form'
const form = useForm({
defaultValues: { issues: [{ title: 'A' }, { title: '' }] },
errorVisibility: () => true,
validators: [
{
triggers: ['change'],
run: ({ value, createErrorMap }) => {
const errors = createErrorMap()
value.issues.forEach((issue, i) => {
if (!issue.title) (errors.fields as Record<string, string>)[`issues[${i}].title`] = 'Required'
})
return errors
},
},
],
})
</script>
<template>
<form.ArrayField name="issues" v-slot="{ field: array }">
<div v-for="(_, i) in array.value" :key="i">
<form.Field :name="`issues[${i}].title`" v-slot="{ field }">
<input :value="field.value" @input="field.handleChange(($event.target as HTMLInputElement).value)" />
<p v-for="error in field.errors" :key="error.message">{{ error.message }}</p>
</form.Field>
<button type="button" @click="array.removeValue(i)">Remove</button>
</div>
</form.ArrayField>
</template>
Steps to reproduce
- Render the component and trigger validation once (for example
form.setFieldValue('issues[0].title', 'A')). Row 1 shows "Required".
- Click Remove on row 0.
- The remaining row is now
issues[0] and form.getFieldMeta('issues[0].title').errors is ["Required"], but no error is rendered.
Expected behavior
After removing an item, the mounted Field for each index follows the field API the form now uses for that name, and renders its current value and errors.
How often does this bug happen?
Every time
Platform
- macOS, Node 24
- Reproduced in Vitest with happy-dom
- Vue 3.6.0-rc.8
TanStack Form adapter
vue-form
TanStack Form version
2.0.0-alpha.2, plus the alpha branch at 6e5b809 with the fix from PR #2392 applied. This issue is separate from that PR.
TypeScript version
6.0.3
Additional context
I haven't included a fix, because it touches how useField identifies its field API and I'd rather hear which direction you prefer. Happy to open a PR.
Describe the bug
With index keys (as in
examples/vue/array), afterarray.removeValue(0)the form creates a new field API instance forissues[0].title, but the mounted<form.Field :name="issues[0].title">keeps the old instance. The form meta forissues[0].titlecontains the error that shifted from the removed row's neighbour, but the slot never renders it.This happens with markup rendered directly inside the
<form.Field>slot. It's unrelated tocreateFormHook.Diagnosis (uses the internal
_tryGetFieldApionly to compare instances):field===form._tryGetFieldApi('issues[0].title')truetruefield=== internal instancefalseform.getFieldMeta('issues[0].title').errors["Required"]useField(src/VueForm/useField.lib.ts) only recreates the field API whenform,nameorresetVersionchanges. AfterremoveValue, the name stays the same, so the component isn't moved to the new instance.Your minimal, reproducible example
Steps to reproduce
form.setFieldValue('issues[0].title', 'A')). Row 1 shows "Required".issues[0]andform.getFieldMeta('issues[0].title').errorsis["Required"], but no error is rendered.Expected behavior
After removing an item, the mounted
Fieldfor each index follows the field API the form now uses for that name, and renders its current value and errors.How often does this bug happen?
Every time
Platform
TanStack Form adapter
vue-form
TanStack Form version
2.0.0-alpha.2, plus the
alphabranch at 6e5b809 with the fix from PR #2392 applied. This issue is separate from that PR.TypeScript version
6.0.3
Additional context
I haven't included a fix, because it touches how
useFieldidentifies its field API and I'd rather hear which direction you prefer. Happy to open a PR.