У React 19 є три підходи до форм, і вони добре поєднуються.
1. Нативна форма + дії (action, useActionState, useFormStatus)
const [state, formAction, isPending] = useActionState(saveSettings, initial);
<form action={formAction}>...</form>
- мінімум коду: значення читаються з
FormData, без стану на кожне поле; - вбудовані стан очікування, черга дій, оптимістичні оновлення;
- прогресивне покращення з Server Functions (Next.js та інші RSC-фреймворки): форма відправляється навіть до завантаження JavaScript, а з
permalinkуuseActionStateбраузер знає, куди перейти; - слабкі місця: валідація на клієнті під час введення, залежні поля, динамічні списки - доводиться дописувати вручну.
Добре для: форм налаштувань, підписки, пошуку, коментарів - коротких форм, де головне - відправка й показ результату.
2. Контрольований стан (useState, useReducer)
- повний контроль над кожним символом: форматування, обчислювані поля, умовна логіка;
- кожне введення - рендер компонента з формою. На великих формах помітно без мемоізації й розбиття на компоненти.
Добре для: невеликих інтерактивних форм з тісною взаємодією полів (калькулятори, конфігуратори).
3. Бібліотека (React Hook Form + Zod, TanStack Form)
- валідація за схемою зі спільним типом, режими перевірки (
onBlur,onChange), фокус на помилці; - масиви полів (
useFieldArray), майстри з кроками, «брудні» поля; - продуктивність великих форм - мінімум рендерів.
Добре для: великих бізнес-форм, адмінок, форм з динамічними частинами.
Як поєднувати:
- бібліотека для стану й валідації, а відправка - у дію (
startTransitionусерединіonSubmit) - щоб отриматиisPendingі оптимістичні оновлення; - нативна форма з
useActionState, а для одного складного поля - контрольований компонент.
Що не залежить від підходу:
- сервер перевіряє все - клієнтська валідація лише прискорює зворотний зв'язок;
- серверні помилки мають потрапити до полів (422 від Laravel чи
errorsвід Inertia); - доступність (мітки,
aria-invalid, фокус) і захист від подвійної відправки; - узгодженість у проєкті: один основний підхід, а не три різні в сусідніх формах.
З Inertia вибір часто вже зроблено: useForm чи компонент <Form> з Inertia дають стан, помилки й відправку з урахуванням маршрутизації Laravel.
Докладніше в документації: form: відправка через Server Function