webroad.online
  1. 1Веб
  2. 2HTML
  3. 3CSS
  4. 4JavaScript
  5. 5TypeScript
  6. 6Git
  7. 7Инструменты
  8. 8React
  9. 9Стейт-менеджмент
  10. 10Next.js
  11. 11Формы
  12. 12Данные и бэкенд
  13. 13SEO
  14. 14Tailwind CSS
  15. 15Анимации
  16. 16Тестирование
  17. 17Архитектура
Формы · Урок 4 из 4

Формы с Server Actions

Полный сценарий, сохранение значений после ошибки и RHF против Conform против TanStack Form.

Обновлено

Что значит форма с Server Action

В Next.js самый прямой способ отправить форму — Server Action: форма отправляет FormData прямо в функцию на сервере, без API-маршрута и без написанного вручную fetch. Этот урок собирает вместе всё изученное: форма → валидация Zod на сервере → ошибки обратно в интерфейс → инвалидация кэша.

Полный сценарий

Если данные неверны, действие возвращает ошибки, и форма их показывает:

формаFormDataaction→Server ActionsafeParse через Zodневерно→UIошибки по полям + сохранённые значения

Если верны, действие сохраняет и обновляет данные:

Server Actionверные данныесохраняет→БДupdateTag / redirect→UIсвежие данные

Реализация

// features/signup/api/signup-action.ts
'use server'

import { z } from 'zod'
import { redirect } from 'next/navigation'
import { signupSchema } from '../model/signup-schema'

export type SignupState = {
  errors?: Partial<Record<keyof z.infer<typeof signupSchema>, string[]>>
  values?: Record<string, string>
  message?: string
}

export async function signup(prev: SignupState, formData: FormData): Promise<SignupState> {
  const raw = Object.fromEntries(formData) as Record<string, string>
  const result = signupSchema.safeParse(raw)
  if (!result.success) return { errors: z.flattenError(result.error).fieldErrors, values: raw }

  const exists = await db.user.findUnique({ where: { email: result.data.email } })
  if (exists) return { errors: { email: ['Аккаунт с таким email уже есть'] }, values: raw }

  await db.user.create({ data: result.data })
  redirect('/welcome')
}
// features/signup/ui/signup-form.tsx
'use client'
import { useActionState } from 'react'
import { signup } from '../api/signup-action'

export function SignupForm() {
  const [state, action, pending] = useActionState(signup, {})
  return (
    <form action={action}>
      <label htmlFor="email">Email</label>
      <input id="email" name="email" defaultValue={state.values?.email} aria-invalid={!!state.errors?.email} aria-describedby="email-error" />
      <p id="email-error" role="alert">{state.errors?.email?.[0]}</p>
      <button disabled={pending}>Создать аккаунт</button>
    </form>
  )
}

Важные детали:

Деталь Почему
возвращаете values и кладёте их в defaultValue после ошибки React сбрасывает форму — без этого пользователь теряет введённое
бизнес-ошибки (email уже есть) в том же формате интерфейс показывает их так же, как ошибки валидации
redirect / updateTag только при успехе read-your-own-writes — см. Кэш
аутентификация + авторизация в начале действия действие — публичный эндпоинт

Валидация и на клиенте — варианты

Сценарий выше проверяет только на сервере: правильно и безопасно, но ошибки пользователь видит лишь после отправки. Для мгновенной обратной связи:

Подход Как Плюсы Минусы
только useActionState как выше, + HTML-атрибуты (required, type) просто, работает без JS ошибки только при отправке
React Hook Form + Server Action RHF проверяет через zodResolver, затем вызываете действие с данными отличный UX, большая экосистема две модели мышления, не работает без JS
Conform создан специально для Server Actions: одна схема, проверка на клиенте и сервере, работает без JS настоящий progressive enhancement меньше сообщество
TanStack Form новая, полностью типизированная, не зависящая от фреймворка очень хорошие типы, быстро многословнее, новее
Formik «классическая» библиотека много старого кода на ней плохо поддерживается — не для новых проектов

Как выбрать

  1. Простая форма (вход, контакты, рассылка) → useActionState + Zod на сервере.
  2. Сложная форма (много полей, динамические списки, живая валидация) → React Hook Form + Zod, с той же схемой в Server Action.
  3. Нужен полный progressive enhancement и проверка с обеих сторон → Conform.

Что бы вы ни выбрали: схема Zod одна, а сервер проверяет всегда.

Коротко

  • Форма → Server Action → safeParse → ошибки + значения обратно или сохранение + updateTag / redirect.
  • Сохраняйте значения после ошибки; бизнес-ошибки — в том же формате.
  • Просто → useActionState; сложно → React Hook Form; progressive enhancement → Conform.

Официальные источники

Упражнения

Была ли страница полезной?

Один клик — без регистрации.