React Hook Form i Zod
Walidacja formularzy z React Hook Form, Zod schema validation, zodResolver i integracja z Next.js Server Actions.
6 bibliotek formularzy i walidacji
React Hook Form, Formik, TanStack Form, Zod, Valibot i TypeBox — porównanie re-renderów, rozmiaru i podejścia do walidacji.
| Biblioteka | Rozmiar | Re-rendery | Walidacja | Kiedy |
|---|---|---|---|---|
| React Hook Form | 9KB | Minimalne (uncontrolled) | Resolver pattern | Standard — używaj domyślnie |
| Formik | 15KB | Każdy keystroke (controlled) | validate / Yup schema | Legacy projekty, Yup preference |
| TanStack Form | 7KB | Fine-grained reactive | Adapter pattern | Framework-agnostic, eksperymentalny |
| Final Form | 5KB | Subscription-based | Ręczna / adapter | Complex dependencies, pub-sub |
| Zod (schema) | 8KB | N/A (walidacja) | Runtime + TypeScript types | Standard do walidacji w RHF |
| Valibot (schema) | 1KB+ | N/A (walidacja) | Modular, tree-shakeable | Bundle-size critical apps |
Często zadawane pytania
Co to jest React Hook Form i dlaczego jest lepszy od Formik?
React Hook Form (RHF): biblioteka formularzy dla React (Bluebill1049, 2019). Filozofia: uncontrolled inputs z register(). Minimalny re-render. Bez Provider. Podstawowe użycie: const {register, handleSubmit, formState: {errors}} = useForm(). input {...register('email', {required: true, pattern: EMAIL_REGEX})}. handleSubmit(data => console.log(data)). Dlaczego lepszy od Formik: Formik: controlled inputs -> re-render przy każdym keystroke. RHF: ref-based -> re-render tylko przy submit/błędzie. Bundle size: RHF 9KB vs Formik 15KB. Performance: RHF zdecydowanie szybszy dla dużych formularzy. API: RHF prostszy (register pattern). Formik: values, touched, handleChange — verbose. TypeScript: RHF natywny TS support. Formik: mniej friendly. RHF zaawansowane: useController — controlled component API. Controller component — dla UI library (shadcn/ui, MUI). useFieldArray — dynamiczne listy. watch() — obserwuj pola. setValue / getValues / reset. trigger() — manualna walidacja. useFormContext — dostęp przez Context w zagnieżdżonych komponentach. FormProvider — provider dla useFormContext. Mode: onChange/onBlur/onSubmit/all — kiedy walidować. shouldFocusError — focus na pierwszym błędzie. Resolver — zewnętrzna walidacja (Zod, Yup, Joi). RHF + shadcn/ui: Form, FormField, FormItem, FormLabel, FormControl, FormMessage — wbudowana integracja.
Zod — runtime schema validation i TypeScript type inference?
Zod: schema-first TypeScript validation library (Colin McDonnell, 2020). Type-safe: schema generuje TypeScript typy. Runtime validation: walidacja danych wejściowych w runtime. Zero dependencies. 8KB gzipped. Podstawy: z.string(), z.number(), z.boolean(), z.date(). z.string().min(3).max(50).email(). z.number().int().positive().min(0).max(100). z.object({name: z.string(), age: z.number()}). Type inference: const UserSchema = z.object({...}). type User = z.infer typeof UserSchema. Zaawansowane typy: z.union([z.string(), z.number()]). z.discriminatedUnion('type', [...]). z.array(z.string()).min(1). z.tuple([z.string(), z.number()]). z.record(z.string(), z.number()). z.enum(['a', 'b', 'c']). z.literal('admin'). z.optional(z.string()) = z.string().optional(). z.nullable(z.string()). z.default('default value'). Transformacje: z.string().transform(val => parseInt(val)). z.preprocess(val => String(val), z.string()). Custom validation: z.string().refine(val => val.includes('@'), 'Must contain @'). z.object({...}).superRefine((data, ctx) => {if (data.password !== data.confirm) ctx.addIssue(...)})). Error handling: schema.safeParse(data) — nie rzuca. result.success, result.data, result.error. schema.parse(data) — rzuca ZodError. z.ZodError.flatten() — flat error map. Zod vs Yup: Zod — TypeScript native, tree-shakeable, szybszy. Yup — starszy, async-first, addMethod dla extensji. Joi — server-side, Node.js, brak TS inference.
React Hook Form + Zod resolver — kompletna integracja?
@hookform/resolvers: bridge między RHF a bibliotekami walidacji. zodResolver, yupResolver, joiResolver. import {zodResolver} from '@hookform/resolvers/zod'. const schema = z.object({email: z.string().email('Invalid email'), password: z.string().min(8, 'Min 8 chars'), age: z.number().min(18, 'Must be 18+')}). type FormData = z.infer typeof schema. const {register, handleSubmit, formState: {errors}} = useForm FormData ({resolver: zodResolver(schema), defaultValues: {email: '', age: 18}}). errors.email?.message — z Zod error messages. Złożone formularze: useFieldArray — dynamiczne pola. const {fields, append, remove, move} = useFieldArray({control, name: 'items'}). fields.map((field, i) => input {...register('items.${i}.name')}). MultiStep forms: FormProvider + useFormContext. Każdy step = osobny komponent. Shared form state przez Context. trigger(['field1', 'field2']) — waliduj tylko dany step. Conditional fields: watch('type') — obserwuj pole. Warunkowa rejestracja innych pól. useWatch — zoptymalizowany, bez re-render parenta. Async walidacja: z.string().refine(async email => checkEmailExists(email), 'Email taken'). mode: 'onBlur' dla async. Server errors: setError('email', {message: 'Already taken'}). clearErrors() po poprawce. Performance tips: defaultValues — zawsze podawaj. useFormContext zamiast prop drilling. formState.isSubmitting — loading state. reset() po sukcesie. resetField() dla konkretnego pola.
Walidacja po stronie serwera — Zod w Next.js Server Actions i API routes?
Zod na serwerze: walidacja danych wejściowych przed przetwarzaniem. Server Actions (Next.js): 'use server'. const schema = z.object({name: z.string().min(1), email: z.string().email()}). async function createUser(formData: FormData) {'use server'. const raw = {name: formData.get('name'), email: formData.get('email')}. const parsed = schema.safeParse(raw). if (!parsed.success) return {errors: parsed.error.flatten().fieldErrors}. prisma.user.create({data: parsed.data})}. API Routes (Next.js): const body = await req.json(). const result = schema.safeParse(body). if (!result.success) return Response.json({errors: result.error.flatten()}, {status: 422}). Middleware validation: Zod w middleware.ts dla route-level. Shared schemas: schemas/ katalog. Importowane przez client i server. Unikaj duplikacji. Toto ze środowiskiem: env schema. z.object({DATABASE_URL: z.string().url(), NODE_ENV: z.enum(['development', 'production', 'test'])}). createEnv (@t3-oss/env-nextjs): bezpieczne zmienne środowiskowe. Zod z tRPC: input: z.object({...}) jako procedura input. Automatyczna walidacja. Type-safe client i server. Zod z Prisma: zod-prisma-types — generuj Zod schemas z Prisma schema. prisma-zod-generator. Eliminuje ręczne pisanie schemas.
Alternatywne biblioteki — Valibot, Effect Schema, TypeBox i arktype?
Landscape bibliotek walidacji 2024: Zod: standard, ogromna adopcja, 8KB. Valibot: modular (tylko to co używasz), tree-shakeable, 1KB+. Prawie identyczne API co Zod. Szybszy. Effect Schema: część Effect ekosystemu (functional TypeScript). Bardzo potężny, ale złożony. TypeBox: JSON Schema + TypeScript. Generuje JSON Schema i typy jednocześnie. Używany w Fastify (TypeBox + Fastify = zero overhead). Arktype: holistyczny, runtime types, operator syntax. const User = type({name: 'string', age: 'number>0'}). Najbardziej ekspresywny syntax. Runtypes: functional style. Jak wybrać: Zod — standard, next-intl, tRPC, RHF. Valibot — bundle size krytyczny. TypeBox — Fastify API. Arktype — nowoczesny syntax. t3-oss/env: typesafe env vars. z.object({}) dla server + client envs. Valibot przykład: import {email, minLength, object, string, parse} from 'valibot'. const Schema = object({email: string([email()]), password: string([minLength(8)])}). Identyczne jak Zod ale tree-shakeable. SuperJSON: serialization (Date, Map, Set, undefined). Używany przez tRPC. Zod + SuperJSON = kompletne rozwiązanie. Konkluzja: Zod to de facto standard 2024. Valibot rosnąca popularność. TypeBox dla performance-critical API.
Powiązane artykuły
Skontaktuj się z nami
Porozmawiajmy o Twoim projekcie. Bezpłatna wycena w ciągu 24 godzin.
Wyślij zapytanie
Telefon
+48 790 814 814
Pon-Pt: 9:00 - 18:00
adam@fotz.pl
Odpowiadamy w ciągu 24h
Adres
Plac Wolności 16
61-739 Poznań
Godziny pracy
Wolisz porozmawiać?
Zadzwoń teraz i porozmawiaj z naszym specjalistą o Twoim projekcie.
Zadzwoń teraz