20.12.2025

React’te Form Validasyonu: Zod + React Hook Form ile Tip Güvenli Akış

Zod şemalarıyla tip güvenli form validasyonu kurun; React Hook Form ile daha az boilerplate, daha net hata yönetimi.

Modern React projelerinde formlar genelde en fazla hata üreten alanlardan biri: doğrulama kuralları dağılır, hata mesajları tutarsızlaşır, tipler formdan API katmanına taşınamaz. Bu yazıda Zod (şema doğrulama) ve React Hook Form (form yönetimi) ile tek bir şemadan hem validasyon hem de TypeScript tiplerini üretmeyi göstereceğim.

Neden bu yaklaşım?

  • Tek doğruluk kaynağı: Kurallar şemada, UI sadece şemayı uygular.
  • Tip güvenliği: z.infer ile form verisi otomatik tiplenir.
  • Temiz hata yönetimi: Alan bazlı hata mesajları standartlaşır.

Kurulum

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

1) Şemayı tanımla (validasyon + tip)

import { z } from "zod";

export const signUpSchema = z .object({ email: z.string().email("Geçerli bir e-posta girin"), password: z .string() .min(8, "En az 8 karakter") .regex(/[A-Z]/, "En az 1 büyük harf") .regex(/[0-9]/, "En az 1 rakam"), confirmPassword: z.string(), marketingConsent: z.boolean().optional(), }) .refine((data) => data.password === data.confirmPassword, { message: "Şifreler eşleşmiyor", path: ["confirmPassword"], });

export type SignUpForm = z.infer;

2) React Hook Form ile bağla

import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { signUpSchema, type SignUpForm } from "./schema";

export function SignUpFormView() { const { register, handleSubmit, formState: { errors, isSubmitting }, } = useForm({ resolver: zodResolver(signUpSchema), defaultValues: { email: "", password: "", confirmPassword: "", marketingConsent: false, }, mode: "onBlur", });

const onSubmit = async (data: SignUpForm) => { // data burada tip güvenli await fetch("/api/signup", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(data), }); };

return (

    E-posta
    
    {errors.email && {errors.email.message}}
  

  
    Şifre
    
    {errors.password && {errors.password.message}}
  

  
    Şifre (tekrar)
    
    {errors.confirmPassword && {errors.confirmPassword.message}}
  

  
    
    Kampanyalardan haberdar olmak istiyorum
  

  
    Kayıt ol

); }

3) Sunucudan gelen hataları form alanlarına bağlamak

API, örneğin email zaten kayıtlı diyebilir. Bunu alan hatasına çevirmek UX’i ciddi iyileştirir:

import type { SubmitHandler } from "react-hook-form";

const onSubmit: SubmitHandler = async (data) => { const res = await fetch("/api/signup", { /* ... */ }); if (!res.ok) { const err = await res.json(); // örnek: { field: "email", message: "Bu e-posta zaten kayıtlı" } if (err.field) { // setError useForm'dan gelir setError(err.field, { type: "server", message: err.message }); return; } } };

Küçük ama kritik ipuçları

  • noValidate: Tarayıcı validasyonunu kapatıp kontrolü şemaya bırakın.
  • Şemayı paylaşın: Aynı Zod şemasını backend’de de kullanabiliyorsanız (Node), “iki kere validasyon yazma” problemi biter.
  • Hata mesajlarını standardize edin: Şemada tek dilden, tek üsluptan ilerleyin.

Sonuç

Zod + React Hook Form ikilisi, form validasyonunu “dağıtık kurallar” olmaktan çıkarıp tip güvenli, bakımı kolay bir akışa dönüştürüyor. Özellikle ekip büyüdükçe, bu yaklaşımın maliyeti değil getirisi artıyor.