Capítulo 22 de 29
TanStack Start, Next.js App Router, and Remix each get a dedicated package (ch002) implementing the same three-step pattern: validate submitted FormData on the server with createServerValidate, run that validation inside that framework's own server-action mechanism, and merge the server result back into the client form with useTransform + mergeForm.
createServerValidate builds a serverValidate function that checks incoming FormData before it's persisted, throwing a ServerValidateError on failure in every one of the three integrations.createServerFn-based server function receives FormData, validates, and returns errors or a success result.'use server') plays the same role, invoked via useActionState on the client.action receives FormData from the POST and is read back via useActionData.getFormDataFromServer() for Start, useActionState's return for Next.js, useActionData for Remix) and merges it into the client form with useTransform + mergeForm, so client-side validation layers cleanly on top of server-validated state instead of overwriting it.| Framework | Server entry point | Client read-back |
|---|---|---|
| TanStack Start | createServerFn + createServerValidate | loader → getFormDataFromServer() |
| Next.js App Router | Server Action ('use server') + createServerValidate | useActionState |
| Remix | route action export + createServerValidate | useActionData |
Across all three frameworks the shape is: define serverValidate = createServerValidate({ ...validation config... }) once, call it inside the framework's server entry point (server function / server action / route action) against the incoming FormData, and catch ServerValidateError to return the failed state back to the client instead of letting it throw uncaught. On the client, useForm is created as usual, then useTransform(mergeForm(...), [serverState]) (or the equivalent read-back for that framework) layers the server's validation result onto the client form's own state — so a page that fails progressive-enhancement (JS not yet loaded, or a slow client) still gets correct server-validated error messages on first paint, and once hydrated, the same form continues validating client-side via the normal validators (ch008) mechanism.
@tanstack/react-form-start/-nextjs/-remix, ch002) — don't hand-roll createServerValidate wiring against the generic core.ServerValidateError is the control-flow signal for "validation failed on the server" — always catch it explicitly rather than letting a validation failure surface as an unhandled 500.mergeForm (ch026) is the piece that actually reconciles server and client state — reach for ch026 when the merge behavior itself (not the framework wiring) is what needs debugging.handleSubmit this server validation complements, not replaces.mergeForm's mechanics in more depth.