Skip to main content
Zod crée des schémas de validation qui s’intègrent avec React Hook Form. Un schéma = validation + types TypeScript automatiques.

Vue d’ensemble

Créer un schéma

On sépare les schémas par feature et par usage. Exemples d’organisation :
Pour les données métier principales :
product-schema.ts
import { z } from "zod";

export const productSchema = z.object({
  name: z.string().min(1, "Le nom est requis"),
  price: z.number().positive("Le prix doit être positif"),
  categoryId: z.string().min(1, "La catégorie est requise"),
  isVisible: z.boolean().default(true),
});

export type Product = z.infer<typeof productSchema>;

Hook de formulaire

Créez un hook personnalisé qui combine Zod et React Hook Form :
use-option-dialog-form.hook.ts
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import {
  optionDialogSchema,
  type OptionDialogForm,
} from "../validations/option-dialog.schema";

export function useOptionDialogForm(initialData?: Partial<OptionDialogForm>) {
  return useForm<OptionDialogForm>({
    resolver: zodResolver(optionDialogSchema),
    defaultValues: {
      name: "",
      minSelections: 0,
      maxSelections: null,
      ...initialData,
    },
  });
}
Le zodResolver connecte automatiquement la validation Zod avec React Hook Form.

Composant formulaire

Utilisez le hook dans votre composant :
OptionDialog.component.tsx
import { useOptionDialogForm } from "../hooks/use-option-dialog-form.hook";
import {
  Form,
  FormField,
  FormItem,
  FormLabel,
  FormControl,
  FormMessage,
} from "@/shared/components/ui/form";
import { Input } from "@/shared/components/ui/input";

export function OptionDialog({ onSubmit, initialData }) {
  const form = useOptionDialogForm(initialData);

  return (
    <Form {...form}>
      <form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
        <FormField
          control={form.control}
          name="name"
          render={({ field }) => (
            <FormItem>
              <FormLabel>Nom de la liste d'options</FormLabel>
              <FormControl>
                <Input placeholder="Entrez le nom" {...field} />
              </FormControl>
              <FormMessage />
            </FormItem>
          )}
        />

        <FormField
          control={form.control}
          name="minSelections"
          render={({ field }) => (
            <FormItem>
              <FormLabel>Sélections minimum</FormLabel>
              <FormControl>
                <Input
                  type="number"
                  min="0"
                  {...field}
                  onChange={(e) => field.onChange(Number(e.target.value))}
                />
              </FormControl>
              <FormMessage />
            </FormItem>
          )}
        />
      </form>
    </Form>
  );
}

Validation dans le service

Validez les données avant de les envoyer au backend :
option.service.ts
import { optionDialogSchema } from "../validations/option-dialog.schema";
import { authClient } from "@/shared/services/backend-client.service";

export const createOption = async (formData: unknown) => {
  // Validation côté client
  const validatedData = optionDialogSchema.parse(formData);

  // Envoi au backend avec données validées
  return authClient.$fetch("/api/options", {
    method: "POST",
    body: validatedData,
  });
};
Utilisez .parse() pour une validation stricte qui lève une erreur si les données sont invalides.

Types de validation courants

// Validation de chaînes
z.string().min(1, "Requis")
z.string().email("Email invalide")
z.string().optional()
z.string().regex(/^[A-Z]+$/, "Majuscules uniquement")

Bonnes pratiques

1

Organisation des fichiers

Séparez les schémas par usage et par feature :
features/catalog/validations/
├── product-schema.ts          # Entité produit
├── product-dialog.schema.ts   # Formulaire création/édition
├── category-schema.ts         # Entité catégorie
└── catalog-dialog.schema.ts   # Formulaire catalog
2

Messages d'erreur clairs

Utilisez des messages en français, spécifiques et actionables : typescript z.string().min(2, "Le nom doit contenir au moins 2 caractères")
3

Validation dans les services

Toujours valider avec .parse() avant l’envoi au backend :
const validatedData = schema.parse(formData);
Cette approche garantit une validation cohérente et type-safe entre le formulaire et l’API.