ViraStack
  • Akademi
  • Hakkında
  • Destek
Genel BakışKaynaklarKatkı
ViraStack

Frontend ekosistemi: ücretsiz rehberler, proje şablonları, AI kuralları ve form bileşenleri.

Ürünler

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

Keşfet

  • Akademi
  • Hakkında
  • Marka
  • Destek

© 2026 ViraStack.

Açık kaynak · MIT

Formlar

05 / 09

Hata gösterimi ve erişilebilirlik

Hata mesajını alana aria-invalid ve aria-describedby ile bağlamak, yeniden kullanılabilir Field component'i, odak, hata özeti, doğrulama zamanlaması, kaymayan düzen ve iyi hata mesajı yazmak.

Hata Görünmek Yetmez

Kırmızı bir metin gören kullanıcı hatayı anlar. Ekran okuyucu kullanan biri için o metin, alana bağlanmadıkça sayfada bir yerde duran bir paragraftır. WCAG iki şey ister: hatalı alan tanımlanmalı ve hata metinle açıklanmalı (3.3.1); düzeltme yolu biliniyorsa önerilmeli (3.3.3).12

Erişilebilir bir hata dört parçadan oluşur:34

ParçaNasıl
Alanın adı<label htmlFor> ile görünür etiket
Hatalı olduğuaria-invalid="true"
Hatanın metniaria-describedby ile alana bağlı mesaj
Görsel ipucuYalnızca renk değil: metin, kenarlık, ikon

Alana Bağlamak

React Hook Form dokümanı aria-invalid ve mesajı birlikte kullanmayı önerir. Ekran okuyucu alanı şöyle okur: "Ad, düzenleme alanı, geçersiz giriş, Ad zorunlu."3

<label htmlFor="email">E-posta</label>
<input
  id="email"
  type="email"
  autoComplete="email"
  aria-invalid={errors.email ? "true" : "false"}
  aria-describedby={errors.email ? "email-error" : undefined}
  {...register("email")}
/>
{errors.email && <p id="email-error">{errors.email.message}</p>}

Alanın bir açıklaması da varsa (örnek biçim, kural) aria-describedby birden fazla id alır; ekran okuyucu ikisini de okur: aria-describedby="phone-hint phone-error".4

İpucu

React Hook Form dokümanı mesajlara role="alert" de ekler. Bu, mesaj göründüğü anda okunmasını sağlar. Çok alanlı bir formda gönderimde on mesaj birden okunur. Alan mesajlarında aria-describedby yeterlidir; role="alert"'ü formun genel hatası gibi tek mesajlara bırakın.34

Field Component'i

Her alanda bu bağlantıyı elle kurmak hata davetiyesidir. Bir Field component'i etiketi, açıklamayı ve hatayı tek yerde toplar. Hata durumunu useFormState ile okur; böylece yalnızca bu alanın hatası değişince render olur:56

// src/components/field.tsx
"use client";

import type { ReactNode } from "react";
import { useFormContext, useFormState } from "react-hook-form";

type FieldA11y = {
  id: string;
  "aria-invalid": boolean;
  "aria-describedby"?: string;
};

type FieldProps = {
  name: string;
  label: string;
  description?: string;
  children: (a11y: FieldA11y) => ReactNode;
};

export function Field({ name, label, description, children }: FieldProps) {
  const { getFieldState } = useFormContext();
  const formState = useFormState({ name });
  const { error } = getFieldState(name, formState);
  const id = name.replaceAll(".", "-");
  const descriptionId = description ? `${id}-description` : undefined;
  const errorId = error ? `${id}-error` : undefined;

  return (
    <div className="flex flex-col gap-1">
      <label htmlFor={id} className="font-medium">
        {label}
      </label>
      {children({
        id,
        "aria-invalid": Boolean(error),
        "aria-describedby": [descriptionId, errorId].filter(Boolean).join(" ") || undefined,
      })}
      {description && (
        <p id={descriptionId} className="text-sm text-gray-600">
          {description}
        </p>
      )}
      {/* Hata için yer ayrılır; mesaj gelince düğmeler kaymaz. */}
      <p id={errorId} className="min-h-5 text-sm text-red-700">
        {error?.message}
      </p>
    </div>
  );
}
<Field name="email" label="E-posta">
  {(a11y) => <input {...a11y} type="email" autoComplete="email" {...register("email")} />}
</Field>

useFormContext için form bir FormProvider ile sarılmalıdır (bkz. Dinamik alanlar ve çok adımlı formlar). Alan adındaki noktalar id'de tireye çevrilir; products.0.name alanının id'si products-0-name olur.

Neden yer ayrılıyor? Hata mesajı alanın altına eklenince altındaki her şey aşağı kayar. Kullanıcı alandan çıkıp bir düğmeye tıklarken blur hatayı gösterir, düğme kayar ve tıklama boşa gider. Bu rehberin proje örneğinde Playwright testleri tam olarak bu yüzden takıldı. Mesaj için sabit bir yükseklik ayırmak sorunu çözer.

Doğrulama Zamanlaması

Hata ne zaman gösterilmeli? Vitaly Friedman yıllar içinde öğrenilenleri şöyle özetler:7

  • Erken doğrulama yapmayın. Kullanıcı daha yazmadan "geçersiz" demek en kötüsüdür.

  • Alandan çıkınca doğrulayın (blur). En yaygın ve dengeli yol.

  • Ödülü erken, cezayı geç verin. Hata düzeltilir düzeltilmez kaybolsun; doğru bir alanı düzenlerken hata ancak alandan çıkınca gelsin.

  • Boş alanları gönderimde kontrol edin. Bir alanı boş geçmek çoğu zaman yanlışlıkla bir tıklamadır.

React Hook Form'daki mode: "onTouched" bu öneriye yakındır: ilk kontrol alandan çıkınca, sonrası her değişiklikte yapılır. Hata yazarken kaybolur.5

GOV.UK Design System daha temkinlidir: alandan çıkınca değil, kullanıcı "Devam" ya da "Gönder"e bastığında doğrulamayı önerir. Gerekçesi, yazmayı bitirmemiş birine hata göstermenin daha çok sorun çıkarmasıdır.8 İki yaklaşım da savunulabilir. Ortak noktaları: kullanıcı yazarken hata göstermeyin.

Gönderimde Odak ve Hata Özeti

Gönderim başarısız olunca kullanıcı hatayı bulmalıdır. React Hook Form varsayılan olarak ilk hatalı alana odaklanır (shouldFocusError: true). Bunun çalışması için alanın ref'i input'a ulaşmalıdır; register'ın döndürdüğü nesneyi input'a yayıyorsanız sorun yok.5

Uzun formlarda W3C ve GOV.UK bir adım daha önerir: formun üstünde bir hata özeti. Özet hataları listeler, her biri alana linktir ve odak özete taşınır:49

import { useEffect, useRef } from "react";
import type { FieldErrors } from "react-hook-form";

type SummaryError = { name: string; message: string };

export function ErrorSummary({ errors, submitCount }: { errors: SummaryError[]; submitCount: number }) {
  const ref = useRef<HTMLDivElement>(null);
  const focusedSubmit = useRef(0);
  const hasErrors = errors.length > 0;

  // Odak her gönderimde bir kez özete taşınır; yazarken çıkan hatalar odağı çalmaz.
  useEffect(() => {
    if (hasErrors && submitCount > focusedSubmit.current) {
      focusedSubmit.current = submitCount;
      ref.current?.focus();
    }
  }, [hasErrors, submitCount]);

  if (!hasErrors) return null;

  return (
    <div ref={ref} tabIndex={-1} role="alert" aria-labelledby="error-summary-title">
      <h2 id="error-summary-title">Bir sorun var</h2>
      <ul>
        {errors.map((error) => (
          <li key={error.name}>
            <a href={`#${error.name.replaceAll(".", "-")}`}>{error.message}</a>
          </li>
        ))}
      </ul>
    </div>
  );
}

export const toSummary = (errors: FieldErrors): SummaryError[] =>
  Object.entries(errors).flatMap(([name, error]) =>
    typeof error?.message === "string" ? [{ name, message: error.message }] : [],
  );
const { formState: { errors, submitCount } } = form;

<ErrorSummary errors={toSummary(errors)} submitCount={submitCount} />

toSummary düz formlar içindir; iç içe alanlarda hataları kendiniz düzleştirin. Linkler alanın id'sine gider; Field'daki id kuralıyla aynı olmalı.

Özet kullanıyorsanız useForm({ shouldFocusError: false }) ile ilk alana odaklanmayı kapatın; aynı anda iki odak hareketi olmasın. GOV.UK ayrıca sayfa başlığının başına "Hata:" eklemeyi önerir; ekran okuyucu sayfayı açar açmaz duyurur.9

İyi Hata Mesajı

GOV.UK Design System'in yazım kuralları formlarda en çok başvurulan kaynaklardan biridir:10

KuralKötüİyi
Ne olduğunu ve nasıl düzeleceğini söyleyinGeçersiz giriş.E-posta adresini ad@ornek.com biçiminde girin.
Boş alanda talimat verinAd alanı boş olamaz.Adınızı girin.
Soruna özel olunHatalı telefon.Telefon numarası 5 ile başlayan 10 haneli olmalı.
Etiketle aynı kelimeleri kullanınBu alan zorunlu.Vergi numarasını girin.
Suçlamayın, özür dilemeyinLütfen geçerli bir değer girmeyi unutmayın!Doğum yılını 4 haneyle girin.

Kural başına ayrı mesaj yazın: boş, çok kısa, çok uzun ve yanlış biçim farklı sorunlardır. Hata gösterdiğinizde alanı temizlemeyin; kullanıcı yazdığını düzeltebilmelidir.10

Etiket ve Otomatik Doldurma

  • Placeholder etiket değildir. Yazmaya başlayınca kaybolur, kontrastı düşüktür. Örnek biçimi açıklama metninde verin.

  • Zorunluluğu etikette belirtin. required özelliği her kullanıcıya alanın zorunlu olduğunu söylemez; görünür etikette belirtin. Formun çoğu zorunluysa isteğe bağlı olanları "(isteğe bağlı)" ile işaretlemek daha az gürültü çıkarır.11

  • autocomplete verin. Ad, e-posta, telefon ve adres alanlarında autocomplete="name", "email", "tel-national" gibi değerler hem otomatik doldurmayı hem WCAG 1.3.5'i (girdi amacını tanımlamak) sağlar.12

Kendin Dene

  • Bir alanın aria-describedby'ını silin ve VoiceOver ya da NVDA ile alana gelin. Hata okunuyor mu?

  • Field'daki min-h-5'i silin, bir alanı boş bırakıp doğrudan altındaki düğmeye tıklayın. Tıklama çalışıyor mu?

  • mode'u onChange yapın ve e-posta yazın. Kaçıncı harfte hata görünüyor?

  • ErrorSummary'yi forma ekleyin, linklere tıklayın ve odağın alana gittiğini kontrol edin.

  • Formunuzdaki her hata mesajını yukarıdaki tabloya göre yeniden yazın.

Detaylı Öğrenmek İçin

  • Resmi doküman: W3C WAI, "User Notification"; hata özeti, satır içi mesaj, başlık ve canlı bölgeler.4

  • Resmi doküman: GOV.UK Design System, "Error message" ve "Error summary"; yerleşim ve yazım kuralları.109

  • Yazı: Vitaly Friedman, "A Complete Guide To Live Validation UX"; doğrulama zamanlaması.7

Kaynaklar

  • 1DokümanW3C: Understanding SC 3.3.1 Error Identification

    Hatalı alanın tanımlanması ve hatanın metinle açıklanması.

  • 2DokümanW3C: Understanding SC 3.3.3 Error Suggestion

    Düzeltme önerisi sunmak.

  • 3DokümanReact Hook Form: Advanced Usage

    `aria-invalid` ve mesajla erişilebilir doğrulama.

  • 4DokümanW3C: User Notification

    Hata listesi, `aria-describedby`, odak, sayfa başlığı ve canlı bölgeler.

  • 5DokümanReact Hook Form: useForm

    `mode: "onTouched"` ve `shouldFocusError`.

  • 6DokümanReact Hook Form: getFieldState

    Alan durumunu `useFormState` ile birlikte okumak.

  • 7MakaleVitaly Friedman

    : — 21 Eylül 2022. Erken ve geç doğrulama, "ödülü erken, cezayı geç ver" ve boş alanlar.

  • 8DokümanGOV.UK Design System: Validation

    Gönderimde doğrulamak ve yazarken doğrulamamak.

  • 9DokümanGOV.UK Design System: Error summary

    Özetin yeri, odak, alanlara link ve "Hata:" başlık öneki.

  • 10DokümanGOV.UK Design System: Error message

    Mesajın yeri ve yazım kuralları.

  • 11MakaleAdrian Roselli

    : — Şubat 2019, güncelleme Ağustos 2023. Zorunluluğu görünür etikette belirtmek ve durumları yardımcı teknolojiye iletmek.

  • 12DokümanW3C: Understanding SC 1.3.5 Identify Input Purpose

    `autocomplete` ile girdi amacını tanımlamak.

Bu sayfayı düzenle

Son güncelleme tarihi: 22 Ekim 2026

Önceki adım04. React Hook Form ile ZodSonraki adım06. Maskeleme ve regex