ViraStack
  • Academy
  • About
  • Support
HomeSourcesContribute
ViraStack

A frontend ecosystem: free guides, project templates, AI rules, and form components.

Products

  • ViraStack Start
  • ViraStack AI
  • ViraStack Mask
  • ViraStack Password
  • ViraStack Guide

Explore

  • Academy
  • About
  • Brand
  • Support

© 2026 ViraStack.

Open source · MIT

Formlar

04 / 09

React Hook Form ile Zod

zodResolver ile şemayı forma bağlamak, kurulumdaki npm uyarısı, tiplerin şemadan çıkması, transform ile input ve output tipleri, alanlar arası kuralda deps ve şemayı sunucuyla paylaşmak.

Neden Birlikte?

React Hook Form temelleri dersinde kuralları register'a yazdınız. Bu yaklaşımın iki sorunu var: kurallar JSX'in içine dağılır ve aynı kuralları sunucuda tekrar yazmanız gerekir. Çözüm kuralları bir Zod şemasına taşıyıp formu bu şemayla doğrulamaktır. Bağlantıyı resolver kurar.12

Kurulum

$ pnpm add react-hook-form zod @hookform/resolvers

Warning

@hookform/resolvers 6.0.0 (19 Ekim 2026) npm 10'da ERESOLVE unable to resolve dependency tree hatası verebilir. Paket desteklediği her doğrulama kütüphanesini isteğe bağlı peer olarak listeler ve npm 10 bunların arasındaki bir sürüm çakışmasını çözemez. Node.js 24 ile gelen npm 11 ve pnpm sorunsuz kurar. npm 10'da kalmanız gerekiyorsa --legacy-peer-deps geçici bir çözümdür.3

zodResolver

Şema bir dosyada, form başka bir dosyada durur:2

// src/features/auth/sign-up-schema.ts
import { z } from "zod";

export const signUpSchema = z
  .object({
    name: z.string().trim().min(2, "Ad en az 2 karakter olmalı."),
    email: z.email("E-posta adresini ad@ornek.com biçiminde girin."),
    password: z.string().min(8, "Şifre en az 8 karakter olmalı."),
    confirmPassword: z.string(),
  })
  .refine((data) => data.password === data.confirmPassword, {
    error: "Şifreler eşleşmiyor.",
    path: ["confirmPassword"],
  });

export type SignUpValues = z.infer<typeof signUpSchema>;
// src/features/auth/sign-up-form.tsx
import { useForm, type SubmitHandler } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { signUpSchema, type SignUpValues } from "./sign-up-schema";

export function SignUpForm() {
  const {
    register,
    handleSubmit,
    formState: { errors },
  } = useForm({
    resolver: zodResolver(signUpSchema),
    defaultValues: { name: "", email: "", password: "", confirmPassword: "" },
    mode: "onTouched",
  });

  const onSubmit: SubmitHandler<SignUpValues> = (values) => createAccount(values);

  return (
    <form onSubmit={handleSubmit(onSubmit)} noValidate>
      <label htmlFor="name">Ad</label>
      <input id="name" autoComplete="name" {...register("name")} />
      {errors.name && <p>{errors.name.message}</p>}

      <label htmlFor="email">E-posta</label>
      <input id="email" type="email" autoComplete="email" {...register("email")} />
      {errors.email && <p>{errors.email.message}</p>}

      <label htmlFor="password">Şifre</label>
      <input
        id="password"
        type="password"
        autoComplete="new-password"
        {...register("password", { deps: "confirmPassword" })}
      />
      {errors.password && <p>{errors.password.message}</p>}

      <label htmlFor="confirmPassword">Şifre tekrar</label>
      <input
        id="confirmPassword"
        type="password"
        autoComplete="new-password"
        {...register("confirmPassword")}
      />
      {errors.confirmPassword && <p>{errors.confirmPassword.message}</p>}

      <button>Hesap oluştur</button>
    </form>
  );
}

useForm'a generic vermediniz; alan adları ve errors'ın tipi zodResolver(signUpSchema)'dan çıkar. register("emial") gibi bir yazım hatası derlemede yakalanır.3

Resolver'la çalışırken üç kural:1

  • Yerleşik kurallar çalışmaz. register("name", { required: true }) resolver varken yok sayılır. Tüm kurallar şemada olmalı.

  • Form düzeyindeki validate çalışmaz. RHF 7.72 ile gelen useForm({ validate }) ve resolver birbirini dışlar; ikisi birden verilirse yalnızca resolver çalışır.

  • Hata mesajı şemadan gelir. Mesajları şemaya yazın; JSX yalnızca errors.x.message'ı gösterir.

Alanlar Arası Kural ve deps

Şifre ve tekrarı eşleştiğinde hata yok. Kullanıcı sonra şifreyi değiştirirse "Şifreler eşleşmiyor." çıkmalı. Çıkmaz: RHF yalnızca değişen alanın hatasını günceller; refine confirmPassword'a yazıyor ama değişen alan password.

deps bu bağı kurar. password doğrulandığında listedeki alanlar da yeniden doğrulanır:4

<input type="password" {...register("password", { deps: "confirmPassword" })} />

Yukarıdaki formda deps bu yüzden var. trigger() ile elle doğrulama yaptığınızda deps uygulanmaz.4

Tip

Zod'da nesnenin .refine'ı, nesnenin herhangi bir alanında tip hatası varken çalışmaz. Seçilmemiş bir z.enum alanı şifre kontrolünü bekletebilir (bkz. Zod ile şema ve doğrulama).

Input ve Output Tipleri

Şemada .transform varsa şemanın girdi ve çıktı tipi ayrılır. Form girdi tipini tutar (input'taki metin), handleSubmit çıktı tipini verir (dönüşmüş değer):35

const productSchema = z.object({
  title: z.string().trim().min(1, "Ürün adı girin."),
  price: z
    .string()
    .regex(/^\d+(,\d{1,2})?$/, "Fiyatı 1250,50 biçiminde girin.")
    .transform((value) => Number(value.replace(",", "."))),
});

type ProductInput = z.input<typeof productSchema>; // { title: string; price: string }
type ProductOutput = z.output<typeof productSchema>; // { title: string; price: number }

export function ProductForm() {
  const { register, handleSubmit } = useForm<ProductInput, unknown, ProductOutput>({
    resolver: zodResolver(productSchema),
    defaultValues: { title: "", price: "" },
  });

  return (
    <form onSubmit={handleSubmit((product) => saveProduct(product))}>
      <input {...register("title")} />
      <input inputMode="decimal" {...register("price")} />
    </form>
  );
}

useForm'un üç generic'i sırasıyla form değerleri, context ve gönderilen değerlerdir. Ya hiç generic vermeyin ve tipi resolver'dan çıkarın, ya da üçünü birden verin. Tek generic useForm<ProductOutput> iki tipi aynıya sabitler ve zodResolver ile çakışır. Resolver dokümanı .default() için de aynı uyarıyı yapar: varsayılan değeri olan alan girdide isteğe bağlı, çıktıda zorunludur.3

Şemayı Sunucuyla Paylaşmak

Şemayı ayrı bir dosyada tutmanın asıl faydası sunucudadır. Tarayıcıdaki doğrulama kullanıcı deneyimi içindir; atlatılabilir. Sunucu aynı şemayla girdiyi tekrar doğrular:67

// app/sign-up/actions.ts
"use server";

import { signUpSchema } from "@/features/auth/sign-up-schema";

export async function createAccount(values: unknown) {
  const parsed = signUpSchema.safeParse(values);
  if (!parsed.success) {
    return { status: "error" as const };
  }
  // parsed.data güvenli
}

Caution

Şemada .transform varsa dikkat: handleSubmit dönüşmüş değeri verir. Bunu aynı şemayla sunucuda tekrar doğrularsanız şema metin bekler ama sayı gelir ve doğrulama başarısız olur. İki yol var: sunucuya gidecek şemada dönüşüm yapmayın ve dönüşümü sunucuda yapın, ya da sunucuya ham FormData gönderin. Ayrıntılar Server Actions ile form dersinde.

Kendin Dene

  • Kayıt formunu kurun. Şifreleri eşleştirin, sonra şifreyi değiştirin. deps'i silip tekrar deneyin.

  • register("name", { required: "Zorunlu" }) ekleyip şemadan min(2)'yi silin. Boş gönderince ne oluyor?

  • ProductForm'da generic'leri silin. product.price'ın tipi ne oluyor? Sonra yalnızca useForm<ProductOutput> yazın ve hatayı okuyun.

  • createAccount'ı formu kullanmadan, bir test butonundan createAccount({ email: "x" }) ile çağırın. Sunucu ne döndürüyor?

Detaylı Öğrenmek İçin

  • Resmi doküman: React Hook Form, "Schema Validation"; resolver ile şema doğrulaması.2

  • Repo: @hookform/resolvers README; Zod örneği ve input/output tipleri.3

  • Resmi doküman: React Hook Form, "useForm: resolver"; resolver kuralları.1

Kaynaklar

  • 1DocsReact Hook Form: useForm

    Resolver, yerleşik kurallarla birlikte kullanılamaması ve form düzeyindeki `validate` ile birbirini dışlaması.

  • 2DocsReact Hook Form: Get Started

    Şema doğrulaması ve resolver kurulumu.

  • 3RepoReact Hook Form: Resolvers

    `zodResolver`, desteklenen kütüphaneler, `.default()` ve üç generic uyarısı.

  • 4DocsReact Hook Form: register

    `deps` seçeneği ve `trigger` ile ilişkisi.

  • 5DocsZod: Transforms

    `.transform`, `z.input` ve `z.output`.

  • 6DocsGOV.UK Design System: Validation

    Her zaman sunucuda doğrulamak, tarayıcı doğrulamasının atlatılabilmesi.

  • 7DocsNext.js: How to create forms with Server Actions

    Server Action'da şema ile doğrulama.

Edit this page

Last updated: October 22, 2026

Previous step03. Zod ile şema ve doğrulamaNext step05. Hata gösterimi ve erişilebilirlik