FormApi falls back to uuid() when no formId is passed:
// packages/form-core/src/FormApi.ts
this._formId = opts?.formId ?? uuid()
That value is random per instance, so the server render and the client render disagree on it. Binding it (id={form.formId}) then produces a hydration mismatch.
React and Preact have always passed an SSR-safe id. #2259 and #2263 added one for Vue and Solid. Three adapters still pass options straight through and never supply a fallback:
| Adapter |
SSR-safe default formId |
| react-form |
yes, React.useId |
| preact-form |
yes, useId |
| vue-form |
yes, since #2259 |
| solid-form |
yes, since #2263 |
| svelte-form |
no |
| angular-form |
no |
| lit-form |
no |
grep -r formId packages/<adapter>/src returns nothing for those three, and each returns the FormApi instance directly, so form.formId is reachable from user code.
SvelteKit is the case I'd worry about, since SSR is on by default there.
Before I send anything: do you want the same treatment for these three? Svelte is the awkward one. $props.id() needs Svelte 5.20 while the peer range is ^5.0.0, and it is a compile-time rune so it cannot be feature-detected off the namespace the way vue-form does with Vue.useId. I would rather agree on the approach than guess. Happy to leave angular and lit out if their hydration does not care about a mismatched id.
FormApifalls back touuid()when noformIdis passed:That value is random per instance, so the server render and the client render disagree on it. Binding it (
id={form.formId}) then produces a hydration mismatch.React and Preact have always passed an SSR-safe id. #2259 and #2263 added one for Vue and Solid. Three adapters still pass
optionsstraight through and never supply a fallback:React.useIduseIdgrep -r formId packages/<adapter>/srcreturns nothing for those three, and each returns theFormApiinstance directly, soform.formIdis reachable from user code.SvelteKit is the case I'd worry about, since SSR is on by default there.
Before I send anything: do you want the same treatment for these three? Svelte is the awkward one.
$props.id()needs Svelte 5.20 while the peer range is^5.0.0, and it is a compile-time rune so it cannot be feature-detected off the namespace the wayvue-formdoes withVue.useId. I would rather agree on the approach than guess. Happy to leave angular and lit out if their hydration does not care about a mismatched id.