Formulaire Next.js : validation côté client et serveur

7 août 2025

Créer un formulaire fiable, validé côté client comme côté serveur, avec enregistrement dans la base de données : c’est un cas d’usage courant. Avec React Hook Form, Zod, Prisma, et les Server Actions de Next.js, on obtient une solution moderne, claire et robuste.

Stack utilisée

  • React Hook Form : gestion du formulaire côté client
  • Zod : validation côté client & serveur
  • Server Actions (Next.js) : traitement et sécurité côté serveur
  • Prisma : interaction avec la base de données

1. Créer le modèle Prisma

Voici un modèle simple qui correspond aux champs du formulaire.

1model User { 2 id Int @id @default(autoincrement()) 3 name String 4 email String @unique 5}

2. Créer le schéma de validation avec Zod

Ce schéma centralise les règles de validation : il est utilisé côté client et côté serveur pour garantir la cohérence.

1import { z } from "zod"; 2 3export const userSchema = z.object({ 4 name: z.string().min(2, "Le nom est trop court"), 5 email: z.string().email("Adresse e-mail invalide"), 6}); 7 8export type UserFormData = z.infer<typeof userSchema>;

3. Créer l’action serveur avec validation

Cette action est appelée depuis le formulaire. Elle vérifie les données côté serveur avec Zod, puis les enregistre avec Prisma.

1"use server"; 2 3import { userSchema } from "@/lib/validation"; 4import { prisma } from "@/lib/prisma"; 5 6export async function createUser(formData: FormData) { 7 const parsed = userSchema.safeParse({ 8 name: formData.get("name"), 9 email: formData.get("email"), 10 }); 11 12 if (!parsed.success) { 13 // On retourne les erreurs sous un format exploitable côté client 14 return { error: parsed.error.flatten().fieldErrors }; 15 } 16 17 const data = parsed.data; 18 19 await prisma.user.create({ 20 data: { 21 name: data.name, 22 email: data.email, 23 }, 24 }); 25 26 return { success: true }; 27}

Pourquoi utiliser FormData ?

Les Server Actions attendent un objet FormData lorsqu’on utilise un formulaire HTML natif ou qu’on veut simplifier l’appel depuis le client.

4. Créer le formulaire avec React Hook Form

On relie chaque champ au schéma Zod via le resolver, et on affiche les erreurs côté client ou venant du serveur.

1"use client"; 2 3import { useForm } from "react-hook-form"; 4import { zodResolver } from "@hookform/resolvers/zod"; 5import { userSchema, UserFormData } from "@/lib/validation"; 6import { createUser } from "@/actions/create-user"; 7import { useState } from "react"; 8 9export default function UserForm() { 10 const [serverErrors, setServerErrors] = useState({}); 11 const { 12 register, 13 handleSubmit, 14 formState: { errors }, 15 } = useForm<UserFormData>({ 16 resolver: zodResolver(userSchema), 17 }); 18 19 const onSubmit = async (data: UserFormData) => { 20 const formData = new FormData(); 21 formData.append("name", data.name); 22 formData.append("email", data.email); 23 24 const result = await createUser(formData); 25 26 if (result?.error) { 27 setServerErrors(result.error); 28 } else { 29 alert("Utilisateur créé !"); 30 } 31 }; 32 33 return ( 34 <form onSubmit={handleSubmit(onSubmit)} className="space-y-4"> 35 <input placeholder="Nom" {...register("name")} /> 36 <p>{errors.name?.message || serverErrors?.name}</p> 37 38 <input placeholder="Email" {...register("email")} /> 39 <p>{errors.email?.message || serverErrors?.email}</p> 40 41 <button type="submit">Envoyer</button> 42 </form> 43 ); 44}

Résultat

  • Validation cohérente et réutilisable (client + serveur)
  • Sécurité garantie grâce aux Server Actions
  • Données enregistrées proprement avec Prisma