React

react-hook-form + schema Zod

zodResolver collega lo schema al form: Zod valida, react-hook-form distribuisce gli errori per campo e z.infer dà il tipo dei valori.

In sintesi

  • zodResolver è il ponte fra lo schema Zod e useForm.
  • handleSubmit esegue il resolver prima di chiamare la funzione di invio.
  • Un errore dello schema arriva già associato al nome del campo.
  • Il resolver gira nel browser: il server deve rivalidare.

Collega Zod a react-hook-form con zodResolver: lo schema valida, il form distribuisce gli errori per campo e il tipo dei valori arriva da z.infer, senza dichiararlo due volte.

Chi valida cosa

Lo schema Zod contiene le regole, il resolver le traduce nel formato di react-hook-form, e il form decide quando eseguirle. Nessuno dei tre duplica la logica degli altri due.

import { z } from "zod";

export const signupSchema = z.object({
  email: z.email(),
  password: z.string().min(12),
});

export type SignupValues = z.infer;

Il form in poche righe

const form = useForm({
  resolver: zodResolver(signupSchema),
  defaultValues: { email: "", password: "" },
});

const onSubmit = form.handleSubmit(async (values) => {
  await fetch("/api/signup", {
    method: "POST",
    body: JSON.stringify(values),
  });
});

handleSubmit esegue prima il resolver: se lo schema fallisce, la funzione non parte e nessuna richiesta lascia il browser. Il corpo JSON che arriva al server è già stata controllato sul client, mai fidato.

Errori per campo


{form.formState.errors.email && (
  {form.formState.errors.email.message}
)}

Il messaggio arriva dallo schema, l’attributo aria-invalid dallo stesso errore: campo e annuncio restano allineati senza codice di collegamento scritto a mano.

Quando validare

  • onSubmit: il predefinito, nessun controllo mentre si digita; l’errore compare al primo invio.
  • onTouched: valida dopo il primo blur e poi a ogni modifica del campo toccato.
  • onChange: valida a ogni tasto; è il più costoso e va scelto per campi con regole leggere.

Il comando

npm install react-hook-form zod @hookform/resolvers

Le tre librerie sono distribuite con licenza MIT; il resolver è il ponte, quindi la versione di Zod deve stare nel range dichiarato da @hookform/resolvers.

Tre record mostrano lo stesso schema di lavoro: multi-step-form valida un passo alla volta, sign-in tiene gli errori accanto ai campi, e la libreria shadcn fornisce i componenti del form.

La limitazione

Il resolver gira solo nel browser: chi costruisce la richiesta a mano lo salta. Lo stesso schema va rieseguito sul server, e z.infer non protegge i valori predefiniti, che restano un oggetto scritto a mano e possono divergere dallo schema.

Dove continuare

Lo schema da solo è in validazione con Zod; la versione con i componenti shadcn è in form shadcn con react-hook-form; il form a più passi è in form multi-step; il confronto fra le librerie è in librerie di form React.

Domande frequenti

Perché serve un resolver?

Perché react-hook-form non conosce Zod: il resolver esegue lo schema e converte gli errori nella struttura che il form sa mostrare.

Devo dichiarare il tipo due volte?

No. Il tipo viene da z.infer sullo schema; useForm lo riceve come parametro generico e i campi restano tipizzati.

Dove va la validazione lato server?

Sul server, con lo stesso schema: il resolver del browser è comodità, non controllo. Il dato che entra va riletto e rivalidato.

Posso validare a ogni tasto?

Sì, con mode onChange. Costa un’esecuzione dello schema per tasto, quindi tienilo per form piccoli o campi con regole semplici.

Cosa succede se lo schema e i default non coincidono?

Il form compila lo stesso, ma un campo mancante resta vuoto e il tipo non lo segnala: i defaultValues vanno scritti a mano e tenuti allineati.