---
title: TanStack Form + Zod Validation في React (2026)
description: 'ابنِ نماذج React بنظام type-safe باستخدام TanStack Form v1 و Zod: التحقق من صحة النموذج والحقول، فحوصات async، حقول المصفوفات، وحالة الإرسال. بدون adapter، كل الكود قابل للتشغيل.'
url: https://nerdleveltech.com/ar/tanstack-form-react-zod-validation-tutorial
language: ar
date: '2026-06-22'
category: frontend
tags:
  - tanstack form
  - zod
  - react
  - forms
  - validation
  - typescript
  - frontend
markdown_url: https://nerdleveltech.com/ar/tanstack-form-react-zod-validation-tutorial.md
attribution: 'Source: NerdLevelTech — https://nerdleveltech.com/ar/tanstack-form-react-zod-validation-tutorial'
terms: https://nerdleveltech.com/llms.txt
---

# TanStack Form + Zod Validation في React (2026)

للتحقق من صحة نموذج TanStack Form باستخدام Zod، قم بتمرير مخطط Zod مباشرة إلى المدقق — `validators: { onChange: schema }` — في خطاف `useForm`. يدعم TanStack Form الإصدار الأول (v1) نظام Standard Schema بشكل أصلي، لذا لم تعد بحاجة لتثبيت أو استيراد محول Zod (adapter). يقوم المخطط بالتحقق من الصحة واستنتاج أنواع (types) النموذج في آن واحد.[^validation]

## ملخص

يدعم TanStack Form إصدار v1.33.0 نظام [Standard Schema](https://standardschema.dev/)، لذا فإن Zod (و Valibot أو ArkType أو Effect/Schema) يتصلون مباشرة — ولم تعد هناك حاجة للمحولات القديمة `@tanstack/zod-form-adapter` و `zodValidator`.[^validation] يبني هذا الدليل العملي نموذج React حقيقيًا من مكون فارغ: مثيل `useForm` يتم التحقق منه بواسطة مخطط Zod 4، وعرض الأخطاء لكل حقل، والتحقق غير المتزامن مع تأخير (debounced async validation)، وحقول المصفوفات الديناميكية، وزر إرسال متصل بـ `canSubmit`. تم فحص كل قصاصة كود برمجياً باستخدام `tsc --noEmit` مقابل `@tanstack/React-form@1.33.0` و `zod@4.4.3` و React 19 في 22 يونيو 2026.

## ما ستتعلمه

- كيفية التحقق من صحة TanStack Form باستخدام Zod (بدون محول، فقط المخطط)
- كيفية إعداد `useForm` و `form.Field` في React من الصفر
- كيفية إظهار أخطاء التحقق بشكل صحيح (أخطاء v1 هي كائنات وليست سلاسل نصية)
- كيفية إضافة التحقق غير المتزامن مع التأخير (debounced async validation) على مستوى الحقل
- كيفية التعامل مع حقول المصفوفات الديناميكية باستخدام `mode="array"`
- متى تختار TanStack Form بدلاً من React Hook Form

## كيف يمكنني التحقق من صحة TanStack Form باستخدام Zod؟

قم بتمرير مخطط Zod مباشرة إلى مفتاح `validators` في `useForm`. لا توجد خطوة محول في الإصدار v1 — يعامل TanStack Form أي مكتبة تتبع Standard Schema، بما في ذلك Zod، كمدقق من الدرجة الأولى ويستخدمها للتحقق من القيم وتحديد أنواع النموذج.[^validation]

قم بتثبيت الحزمتين (يجلب TanStack Form نواته الخاصة؛ ما عليك سوى إضافة Zod):

```bash
npm install @tanstack/React-form@1.33.0 zod@4.4.3
```

الآن حدد مخططًا وقم بتزويد النموذج به. لاحظ أسلوب Zod 4 في `z.email()` — المساعد عالي المستوى الذي يحل محل سلسلة `z.string().email()` القديمة:

```tsx
import { useForm } from '@tanstack/React-form'
import { z } from 'zod'

const schema = z.object({
  email: z.email('Enter a valid email'),
  password: z.string().min(8, 'At least 8 characters'),
})

export function SignupForm() {
  const form = useForm({
    defaultValues: { email: '', password: '' },
    validators: { onChange: schema },
    onSubmit: async ({ value }) => {
      // value is fully typed: { email: string; password: string }
      console.log('submit', value)
    },
  })

  return (
    <form
      onSubmit={(e) => {
        e.preventDefault()
        e.stopPropagation()
        form.handleSubmit()
      }}
    >
      {/* fields go here */}
    </form>
  )
}
```

يقوم `validators.onChange` بتشغيل المخطط عند كل تغيير؛ استبدله بـ `onBlur` أو `onSubmit` للتحقق لاحقًا. نظرًا لأن المخطط يصف أيضًا شكل البيانات، فإن `value` داخل `onSubmit` يتم تعريف نوعها كـ `{ email: string; password: string }` دون الحاجة إلى generics إضافية.[^quickstart]

## كيف يمكنني إعداد form.Field في React؟

`form.Field` هو مكون يعتمد على render-prop: تمنحه `name` ودالة تستقبل حالة الحقل ومعالجاته. هذا يجعل كل مدخل مشتركًا فقط في الجزء الخاص به من حالة النموذج، لذا فإن الكتابة في حقل واحد لا تؤدي إلى إعادة رندر الحقول الأخرى.[^quickstart]

أضف حقلاً داخل `<form>` من القسم السابق:

```tsx
<form.Field name="email">
  {(field) => (
    <div>
      <label htmlFor={field.name}>Email</label>
      <input
        id={field.name}
        value={field.state.value}
        onBlur={field.handleBlur}
        onChange={(e) => field.handleChange(e.target.value)}
      />
    </div>
  )}
</form.Field>
```

الأجزاء الثلاثة التي تربطها في كل مدخل هي `field.state.value` (القيمة الحالية)، و `field.handleChange` (لتحديثها)، و `field.handleBlur` (لتمييزها كـ "touched"). يتم فحص `name` برمجياً مقابل `defaultValues` الخاصة بك، لذا فإن أي خطأ إملائي مثل `name="emial"` سيؤدي لخطأ أثناء الكومبايل، وليس خطأ صامتاً أثناء التشغيل.

## كيف يمكنني إظهار أخطاء التحقق في TanStack Form؟

اقرأ `field.state.meta.errors` — ولكن في الإصدار v1، كل خطأ من مدقق Standard Schema هو **كائن مشكلة (issue object)**، لذا قم برندر `error.message`، وليس القيمة الخام. هذا خطأ شائع عند الانتقال من الأمثلة القديمة القائمة على المحولات، والتي كانت تعرض الأخطاء كسلاسل نصية بسيطة.[^validation]

```tsx
<form.Field name="email">
  {(field) => (
    <div>
      <input
        value={field.state.value}
        onBlur={field.handleBlur}
        onChange={(e) => field.handleChange(e.target.value)}
      />
      {!field.state.meta.isValid && (
        <em role="alert">
          {field.state.meta.errors.map((err) => err?.message).join(', ')}
        </em>
      )}
    </div>
  )}
</form.Field>
```

استخدم `field.state.meta.isValid` لتحديد ما إذا كنت ستعرض الرسالة، وقم بعمل map على `errors` باستخدام `err?.message` لأن المصفوفة تحتوي على كائنات. (إذا كتبت بدلاً من ذلك مدقق دالة بسيط يرجع سلسلة نصية، فستكون تلك السلسلة هي الخطأ مباشرة — انظر القسم التالي. ينتج عن أسلوبي المدقق شكلين مختلفين للأخطاء، وهو ما يربك البعض.)

## كيف يمكنني تعطيل زر الإرسال حتى يصبح النموذج صالحًا؟

قم بلف الزر في `form.Subscribe` واختر `canSubmit`. يقوم `form.Subscribe` بإعادة الرندر فقط عندما يتغير الجزء الذي اخترته، لذا يتفاعل الزر مع حالة الصلاحية والإرسال دون إعادة رندر النموذج بالكامل.[^quickstart]

```tsx
<form.Subscribe selector={(state) => [state.canSubmit, state.isSubmitting]}>
  {([canSubmit isSubmitting]) => (
    <button type="submit" disabled={!canSubmit}>
      {isSubmitting ? 'Submitting…' : 'Create account'}
    </button>
  )}
</form.Subscribe>
```

يعكس `canSubmit` ما إذا كان يمكن إرسال النموذج — ويصبح `false` بمجرد فشل أحد المدققات أو إذا كان التحقق غير المتزامن لا يزال قيد التشغيل — و `isSubmitting` يكون `true` أثناء تنفيذ وعد (promise) `onSubmit`، وهو ما يكفي لتعطيل الزر وإظهار مؤشر تحميل دون تتبع تلك الحالة بنفسك.

## كيف يمكنني إضافة التحقق غير المتزامن في TanStack Form؟

أضف مدقق `onChangeAsync` (بالإضافة إلى `onChangeAsyncDebounceMs`) على مستوى الحقل لعمليات التحقق التي تتطلب طلباً عبر الشبكة، مثل "هل اسم المستخدم هذا مأخوذ؟". يمكن تأخير المدققات غير المتزامنة — اضبط `onChangeAsyncDebounceMs` حتى لا تطلق طلباً مع كل ضغطة مفتاح — وهي تعمل فقط بعد اجتياز المدققات المتزامنة.[^validation]

```tsx
<form.Field
  name="username"
  validators={{
    onChange: ({ value }) =>
      value.length < 3 ? 'Too short' : undefined,
    onChangeAsyncDebounceMs: 500,
    onChangeAsync: async ({ value }) => {
      const res = await fetch(`/API/username-available?u=${value}`)
      const { available } = await res.json()
      return available ? undefined : 'Username is taken'
    },
  }}
>
  {(field) => (
    <div>
      <input
        value={field.state.value}
        onChange={(e) => field.handleChange(e.target.value)}
      />
      {field.state.meta.isValidating && <span>Checking…</span>}
      {!field.state.meta.isValid && (
        <em>{field.state.meta.errors.join(', ')}</em>
      )}
    </div>
  )}
</form.Field>
```

هنا المدققات هي دوال بسيطة **ترجع سلسلة نصية** (أو `undefined` عندما تكون صالحة)، لذا فإن `errors` تحتوي على سلاسل نصية وتقوم برندرها مباشرة — بدون `.message`. يكون `field.state.meta.isValidating` هو `true` أثناء تشغيل التحقق غير المتزامن، وهو ما تربط به مؤشر "Checking…".

## كيف يمكنني التعامل مع حقول المصفوفات في TanStack Form؟

امنح الـ `form.Field` الأب الخاصية `mode="array"`، ثم قم برندر حقل ابن واحد لكل عنصر باستخدام اسم مفهرس مثل `guests[${i}].name`. أضف الصفوف وأزلها باستخدام `field.pushValue()` و `field.removeValue()` — ستقوم القائمة بإعادة رندر نفسها.[^arrays]

```tsx
<form.Field name="guests" mode="array">
  {(field) => (
    <div>
      {field.state.value.map((_, i) => (
        <form.Field key={i} name={`guests[${i}].name`}>
          {(sub) => (
            <input
              value={sub.state.value}
              onChange={(e) => sub.handleChange(e.target.value)}
            />
          )}
        </form.Field>
      ))}
      <button type="button" onClick={() => field.pushValue({ name: '' })}>
        Add guest
      </button>
      <button
        type="button"
        onClick={() => field.removeValue(field.state.value.length - 1)}
      >
        Remove last
      </button>
    </div>
  )}
</form.Field>
```

يقوم مخطط Zod مثل `z.array(z.object({ name: z.string().min(1) })).min(1)` بالتحقق من صحة المصفوفة بأكملها على مستوى النموذج، لذا فإن "أضف ضيفًا واحدًا على الأقل" و "كل ضيف يحتاج إلى اسم" كلاهما يأتي من نفس المخطط الذي كتبته بالفعل.

## متى يجب أن أستخدم TanStack Form بدلاً من React Hook Form؟

اختر TanStack Form عندما تريد مسارات حقول آمنة برمجياً (type-safe)، وتحققاً غير متزامن مدمجاً، ونموذجاً ذهنياً واحداً مشتركاً مع TanStack Query أو Router؛ واستمر في استخدام React Hook Form عندما يكون لديك نماذج بسيطة إلى متوسطة وتُقدّر نضجها ونظامها البيئي الضخم.[^comparison] لا يوجد خيار "أفضل" بشكل مطلق — فكل منهما يقدم مقايضات مختلفة.

يعتمد React Hook Form على المراجع (ref-based) وغير خاضع للتحكم (uncontrolled)، مما يحافظ على انخفاض إعادة التصيير (re-renders) وصغر حجم الـ API، كما يمتلك سنوات من الاعتماد والتكاملات خلفه. أما TanStack Form فيستخدم مخزناً تفاعلياً (reactive store) مع اشتراكات دقيقة، وأسماء حقول مكتوبة بالكامل (fully typed)، ونواة مستقلة عن إطار العمل (React، Vue، Solid، Svelte، Angular)، وتحقق Standard Schema مدمج.[^comparison] إذا كانت نماذجك تعمل بشكل جيد ولا تواجه مشاكل في أمان الأنواع (type-safety) أو التحقق غير المتزامن، فلا يوجد داعٍ للاستعجال في التغيير.

## الأسئلة الشائعة

**هل لا تزال بحاجة إلى محول Zod لـ TanStack Form؟** لا. يدعم TanStack Form v1 نظام Standard Schema بشكل أصلي، لذا يمكنك تمرير مخطط Zod مباشرة إلى المدقق. حزمة `@tanstack/zod-form-adapter` المنفصلة ومدققها `zodValidator` لا يُستخدمان في الإصدار v1 — الأمثلة التي تستوردهما قديمة.[^validation]

**كيف يمكنني التحقق من نموذج TanStack Form باستخدام Zod؟** قم بتعريف مخطط Zod وتعيينه لـ `validators.onChange` (أو `onBlur`/`onSubmit`) في `useForm`. يقوم المخطط بالتحقق من القيم واستنتاج أنواع النموذج في خطوة واحدة.[^validation]

**لماذا تظهر أخطاء TanStack Form كـ `[object Object]`؟** لأن مدققات Standard Schema تُرجع كائنات مشكلات (issue objects) وليس نصوصاً. قم بتصيير `error.message` لكل إدخال في `field.state.meta.errors`. في المقابل، تُرجع مدققات الدوال العادية نص الخطأ مباشرة.[^validation]

**كيف يمكنني إضافة تحقق غير متزامن (async validation)؟** استخدم `onChangeAsync` في `validators` الخاصة بالحقل، واضبط `onChangeAsyncDebounceMs` لتقليل تكرار التنفيذ (debounce). يتم تشغيل الفحص غير المتزامن فقط بعد اجتياز المدققات المتزامنة؛ اربط `field.state.meta.isValidating` بمؤشر تحميل.[^validation]

**كيف يمكنني إضافة أو إزالة عناصر في حقل مصفوفة (array field)؟** اضبط `mode="array"` على الحقل الأب واستدعِ `field.pushValue(item)` و `field.removeValue(index)`. قم بتصيير العناصر التابعة بأسماء مفهرسة مثل `items[${i}].value`.[^arrays]

**ما هو إصدار TanStack Form الحالي؟** الإصدار `@tanstack/React-form@1.33.0` هو أحدث إصدار اعتباراً من 22 يونيو 2026، مع نطاق توافق لـ React هو `^17 || ^18 || ^19`.

## الخلاصة

يجعل TanStack Form v1 من Zod مدققاً من سطر واحد: مرر المخطط، صير `err.message`، واترك `canSubmit` تتحكم في الزر — بدون محول، وبدون كود ربط إضافي. ابنِ النموذج المتزامن أولاً، ثم أضف طبقات الفحص غير المتزامن (debounced) وحقول المصفوفات مع نمو نموذجك. إذا كنت تعمل عبر نظام TanStack البيئي، فراجع [دليل التجهيز المسبق (prefetch) لـ TanStack Query في Next.js App Router](https://nerdleveltech.com/tanstack-query-nextjs-app-router-prefetch-tutorial) وشرحنا لـ [بارامترات البحث الآمنة برمجياً مع TanStack Router و Zod](https://nerdleveltech.com/tanstack-router-type-safe-search-params-zod-tutorial). للحصول على رؤية أخرى حول إرسال النماذج وواجهة المستخدم قيد الانتظار، قارن هذا مع [أفعال الخادم (server actions) في React 19 وواجهة المستخدم المتفائلة (optimistic UI)](https://nerdleveltech.com/nextjs-16-server-actions-React-19-optimistic-ui-tutorial).

[^validation]: TanStack Form — Form and Field Validation (React). <https://tanstack.com/form/latest/docs/framework/React/guides/validation>

[^quickstart]: TanStack Form — Quick Start (React). <https://tanstack.com/form/latest/docs/framework/React/quick-start>

[^arrays]: TanStack Form — Arrays (React). <https://tanstack.com/form/v1/docs/framework/React/guides/arrays>

[^comparison]: TanStack Form — Comparison. <https://tanstack.com/form/latest/docs/comparison>
