Wiring React Hook Form to Zod

A resolver is the function React Hook Form 351,018 calls to validate the whole form. zodResolver(schema) runs safeParse and files each issue in formState.errors under its path. @hookform/resolvers 5.9.1 accepts Zod 3 44,027 and Zod 4 schemas and infers the form's types from the schema, so useForm needs no type argument:

src/app/checkout.tsx: the form, validated by the schema (excerpt)TSX
  const { control, handleSubmit, setError, formState } = useForm({
    resolver: zodResolver(checkoutSchema),
    mode: 'onTouched',                        // first check on blur, then on every change
    defaultValues: { name: user ?? '', email: '', delivery: 'ship' as const,
      address: '', postcode: '' },
  });
  const { errors, isSubmitting } = formState;
  const delivery = useController({ control, name: 'delivery' }).field;

Give every field a default (reset() returns to them). Without as const, TypeScript widens delivery to string and npx tsc --noEmit rejects the resolver line: "Type 'string' is not assignable to type '"ship" | "pickup"'". With a resolver, skip per-field rules: the schema is the single source of truth, and it can run again on the server, where validation actually protects your data.