image: “https://media2.dev.to/dynamic/image/width=1200,height=627,fit=cover,gravity=auto,format=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdrogrsknm00cerv5a9pu.png” pubDate: 2026-07-17T12:28:51.073Z
Когда последний раз вам приходилось писать очередной /api/submit-form эндпоинт только для того, чтобы сохранить данные из простой формы? React 19 предлагает альтернативу — Server Actions, которые позволяют вызывать серверные функции прямо из клиентских компонентов. Но работает ли это в реальных проектах или это очередной overhyped feature?
От API endpoints к Server Actions: что изменилось
Традиционный флоу работы с формами в React выглядит так:
- Клиентский компонент собирает данные
- Отправляет их через
fetchили axios на API route - Обрабатывает loading/error/success состояния
- Обновляет UI после ответа
С Server Actions схема становится проще:
// Клиентский компонент
<form action={serverAction}>
{/* поля формы */}
</form>
// Серверная функция
"use server"
export async function serverAction(formData) {
// работа с БД прямо здесь
}
На практике это означает, что:
- Нет ручного управления состояниями загрузки (hello,
useActionState) - Серверный код можно вызывать как обычные функции (но под капотом — POST-запрос)
- TypeScript типы работают сквозь клиент-серверную границу
Но есть и нюансы: например, нельзя просто передать произвольные данные — только то, что сериализуется в JSON. Это значит, что классы, функции или циклические ссылки вызовут ошибки.
Как это работает в продакшне: разбираем кейс
Допустим, у нас есть Next.js-приложение с формой добавления товара. Вот как выглядит реализация с Server Actions:
// actions/product.ts
"use server"
import { db } from "./db"
export async function addProduct(prevState: any, formData: FormData) {
const name = formData.get("name")
const price = formData.get("price")
// Валидация
if (!name || name.length < 3) {
return { error: "Name must be at least 3 chars" }
}
try {
const product = await db.products.create({
data: { name, price: Number(price) }
})
return { success: true, product }
} catch (err) {
return { error: "Failed to create product" }
}
}
Клиентская часть:
// components/AddProductForm.tsx
"use client"
import { useActionState } from "react"
import { addProduct } from "@/actions/product"
export function AddProductForm() {
const [state, action, isPending] = useActionState(addProduct, null)
return (
<form action={action}>
<input name="name" />
<input name="price" type="number" />
<button disabled={isPending}>
{isPending ? "Saving..." : "Add Product"}
</button>
{state?.error && <div className="error">{state.error}</div>}
</form>
)
}
Что здесь важно:
- Серверный код изолирован — он никогда не попадает в клиентский бандл
useActionStateавтоматически обрабатывает pending состояния- Форма остается интерактивной даже без JavaScript (progressive enhancement)
Когда Server Actions — не лучший выбор
Несмотря на удобство, есть сценарии, где Server Actions могут усложнить архитектуру:
- Комплексные API: если ваш бэкенд должен обрабатывать WebSockets, Server-Sent Events или другие не-HTTP протоколы
- Микросервисы: когда серверная логика живет в отдельных сервисах, а не в Next.js API routes
- Строгий контроль над запросами: если нужно тонко настраивать headers, cache или CORS
В таких случаях традиционные API endpoints все еще выигрывают.
Что попробовать в следующих проектах
- Комбинируйте Server Actions с
useOptimisticдля мгновенного UI-ответа - Добавьте Zod для валидации данных на сервере
- Поэкспериментируйте с прогрессивным улучшением — отключите JavaScript и проверьте, как работает форма
Server Actions — это не серебряная пуля, но для многих CRUD-операций они действительно сокращают бойлерплейт. Главное — не пытаться запихнуть в них всю бизнес-логику и помнить про ограничения сериализации.
Источник: https://dev.to/banti_kevat_8e2d123bb7994/react-server-actions-complete-guide-react-19-in-hindi-4k9l