# Server Actions у Next.js 16 у 2026: мутації, ревалідація та питання співбесід
> Як Server Actions у Next.js 16 обробляють мутації, ревалідацію, стан очікування, оптимістичний інтерфейс і безпеку, разом із питаннями співбесід на кожну концепцію.
- Published: 2026-06-24
- Updated: 2026-07-06
- Author: SharpSkill
- Tags: next.js, server-actions, react, mutations, revalidation, interview
- Reading time: 11 min
---
Server Actions у Next.js перетворюють звичайну асинхронну функцію на серверний ендпоінт для мутацій, який форма може викликати напряму — без API-маршруту, без клієнтського `fetch` і без ручної серіалізації JSON. У Next.js 16 це стандартний спосіб писати мутації даних, і вони зустрічаються майже на кожній співбесіді для senior-розробників React у 2026 році. Цей детальний розбір пояснює, як вони виконуються, як ревалідація доносить свіжі дані до інтерфейсу, як моделювати стани очікування й помилок, а також правила безпеки, на яких спотикається більшість розробників.
> **Визначення в одному реченні**
>
> Server Action — це асинхронна функція, позначена директивою `"use server"`, яка виконується лише на сервері й може викликатися з клієнтського компонента або форми так, ніби вона локальна, тоді як Next.js автоматично бере на себе мережевий запит, серіалізацію аргументів і захист від CSRF.
## Як Server Actions виконуються під капотом
Server Action — це функція, що виконується виключно на сервері та викликається через мережу за допомогою ендпоінта, згенерованого компілятором. Коли функція має директиву `"use server"`, компілятор Next.js ніколи не надсилає її тіло до браузера. Натомість він замінює функцію на легке посилання: хешований ідентифікатор дії, прив'язаний до згенерованого POST-маршруту. Виклик дії з клієнта надсилає запит на цей маршрут, справжня функція виконується на сервері, а серіалізований результат стрімиться назад до того, хто її викликав.
Такий дизайн має три наслідки, які варто пам'ятати як для продакшн-коду, так і для співбесід. По-перше, аргументи та значення, що повертаються, мають бути серіалізовними, бо вони перетинають мережу у вигляді закодованих даних. По-друге, секрети залишаються на сервері, оскільки тіло функції ніколи не потрапляє до клієнтського бандла. По-третє, дії, прив'язані до `
)
}
```
Атрибут `name` кожного поля стає ключем, який зчитує `formData.get()`. [Довідник MDN щодо FormData](https://developer.mozilla.org/en-US/docs/Web/API/FormData) пояснює, як браузер складає цей об'єкт із форми. Для гнучкішого керування `next/form` розширює нативний елемент клієнтською навігацією та передзавантаженням, зберігаючи той самий контракт на основі дій.
## Керування станами очікування та помилок за допомогою useActionState
Справжнім формам потрібен зворотний зв'язок для валідації та індикатор завантаження. Хук `useActionState` із React 19 обгортає Server Action, проводить значення стану крізь кожне надсилання та надає прапорець `isPending`. Він замінює стару назву `useFormState` і повертає кортеж із поточного стану, обгорнутої дії та булевого значення очікування.
```tsx
// app/posts/new-post-form.tsx
"use client"
import { useActionState } from "react"
import { createPost } from "./actions"
const initialState = { error: "" }
export default function NewPostForm() {
// state carries the last return value; isPending tracks the in-flight request
const [state, formAction, isPending] = useActionState(createPost, initialState)
return (
)
}
```
Коли дією керує хук, сигнатура функції отримує перший параметр `prevState` перед `FormData`. Повернення об'єкта замість викидання винятку дозволяє компоненту показувати вбудовану валідацію без error boundary:
```typescript
// app/posts/actions.ts
"use server"
import { revalidateTag } from "next/cache"
import { PostService } from "@/lib/services/post-service"
type FormState = { error: string }
export async function createPost(
prevState: FormState,
formData: FormData,
): Promise {
const title = formData.get("title") as string
// Validate on the server; client validation is never trustworthy alone
if (!title || title.length < 3) {
return { error: "Title must be at least 3 characters." }
}
await PostService.create({ title })
// Invalidate by tag rather than path for finer-grained control
revalidateTag("posts")
return { error: "" }
}
```
[Довідник useActionState на react.dev](https://react.dev/reference/react/useActionState) охоплює складніші патерни, наприклад підтримку permalink для надсилань до гідратації. Повернення структурованого стану тримає успішний і помилковий сценарії в одному передбачуваному місці.
## revalidatePath проти revalidateTag: вибір правильної інвалідації
Мутація — це лише половина справи; інтерфейс має її відобразити. Next.js надає дві функції інвалідації з `next/cache`, і вибір правильної — часте питання, що розрізняє кандидатів на співбесіді.
| Функція | Що інвалідовує | Коли використовувати |
|----------|-------------|-----------|
| `revalidatePath("/posts")` | Кожен кешований запис для маршруту | Мутація впливає на одну конкретну сторінку чи layout |
| `revalidateTag("posts")` | Кожен кешований fetch із цим тегом | Ті самі дані з'являються на кількох маршрутах |
`revalidatePath` — грубий та орієнтований на маршрут: він очищає кеш для URL і його дерева layout. `revalidateTag` — точковий та орієнтований на дані: будь-який `fetch` чи кешована функція, позначені рядком-тегом, скидаються, де б вони не перебували. Теги краще масштабуються у великих застосунках, бо одна мутація може оновити бічну панель, список і сторінку деталей за один виклик. Обидві функції позначають дані як застарілі, а не перезавантажують їх одразу, тож наступний запит регенерує вміст.
У Cache Components із Next.js 16 теги інтегруються з директивою `"use cache"` через `cacheTag`, що змінює підхід до кешування в межах усього застосунку. Цю взаємодію детально розглянуто в [посібнику з Cache Components у Next.js 16](/blog/react-next/nextjs-16-cache-components-use-cache-ppr-interview-questions) на SharpSkill.
## Оптимістичні оновлення інтерфейсу за допомогою useOptimistic
Мережеві звернення додають помітну затримку. Хук `useOptimistic` миттєво відображає очікуваний результат, а потім узгоджує його з відповіддю сервера після завершення дії. Він приймає поточний стан і редюсер, що створює оптимістичну версію.
```tsx
// app/posts/like-button.tsx
"use client"
import { useOptimistic } from "react"
import { likePost } from "./actions"
export function LikeButton({ postId, likes }: { postId: string; likes: number }) {
// optimisticLikes updates before the server confirms
const [optimisticLikes, addOptimisticLike] = useOptimistic(
likes,
(current) => current + 1,
)
async function handleLike() {
addOptimisticLike(null) // update the UI immediately
await likePost(postId) // then run the real mutation
}
return (
)
}
```
Якщо дія завершується невдало, React автоматично повертає оптимістичне значення до підтвердженого серверного стану, тож ручний відкат не потрібен. [Довідник useOptimistic на react.dev](https://react.dev/reference/react/useOptimistic) детально описує, як цикл узгодження взаємодіє з transitions. Саме цей патерн робить форми на Server Actions такими ж чутливими, як повністю клієнтський SPA.
## Прив'язка аргументів і виклик дій з обробників подій
Не кожна мутація починається з надсилання форми. Видалення рядка, перемикання налаштування чи зміна порядку списку часто спрацьовують від кліку по кнопці або іншої події. Server Action — це звичайне посилання на функцію на клієнті, тож її можна викликати з будь-якого обробника, за умови що виклик відбувається всередині transition, щоб інтерфейс залишався чутливим.
Додаткові аргументи, які не є полями форми, приєднуються за допомогою `bind`, що створює нову дію з цими значеннями на початку. Це ідіоматичний спосіб передати ідентифікатор разом із надісланими `FormData`.
```tsx
// app/posts/post-actions.tsx
"use client"
import { useTransition } from "react"
import { deletePost, updatePost } from "./actions"
export function PostActions({ postId }: { postId: string }) {
const [isPending, startTransition] = useTransition()
// Bind the id so the action receives it before FormData
const updateWithId = updatePost.bind(null, postId)
return (
{/* Event-handler invocation wrapped in a transition */}
)
}
```
Прив'язані аргументи шифруються, як і будь-яке інше замкнуте значення, тож `postId` не можна підмінити під час передавання, хоча дія все одно повинна перевірити, що той, хто її викликає, має право діяти з цим записом. Обгортання прямого виклику в `startTransition` дозволяє React тримати інтерфейс інтерактивним і показувати стан очікування без обгортання у форму.
## Безпека Server Actions: кожна дія — це публічний API
Найнебезпечніша хибна думка полягає в тому, що Server Action є приватною, бо написана поряд із серверним кодом. Насправді, щойно дія використовується, Next.js генерує публічний POST-ендпоінт, який будь-хто може викликати сформованим запитом. Фреймворк надає безпечні ідентифікатори дій і видаляє невикористані дії під час `next build`, але він не авторизує того, хто викликає.
> **Авторизація всередині кожної дії**
>
> Server Actions — це доступні HTTP-ендпоінти. Перевірки сесії в layout чи middleware не захищають їх, бо зловмисник може надіслати POST безпосередньо до дії. Сесію та права того, хто викликає, слід перевіряти всередині тіла дії, перш ніж торкатися будь-яких даних.
```typescript
// app/admin/actions.ts
"use server"
import { auth } from "@/lib/auth"
import { headers } from "next/headers"
import { UserService } from "@/lib/services/user-service"
export async function deleteUser(userId: string) {
// Authenticate on every invocation, not in a wrapper component
const session = await auth.api.getSession({ headers: await headers() })
if (!session || session.user.role !== "admin") {
throw new Error("Unauthorized")
}
await UserService.delete(userId)
}
```
Ще два правила мають значення. Замкнуті значення, захоплені вбудованою дією, шифруються перед надсиланням клієнту й розшифровуються на сервері, тож захоплений токен не витікає у відкритому вигляді, але покладатися на це ненадійно: секрети взагалі не варто тримати в замиканнях. І будь-який аргумент, що надходить від клієнта, є недовіреним вводом, який потрібно валідувати, точно як тіло REST-запиту. [Посібник Next.js з безпеки даних](https://nextjs.org/docs/app/guides/data-security) формалізує модель taint, що стоїть за цими гарантіями. Для структурованої практики цих компромісів [модуль співбесід із Server Actions у Next.js](/technologies/react-next/interview-questions/nextjs-server-actions) на SharpSkill відпрацьовує саме ті запитання, які ставлять комісії з найму.
## Питання для співбесіди щодо Server Actions у Next.js 16
Ці питання регулярно зринають на співбесідах для senior-розробників React і full-stack у 2026 році.
**Чому Server Action може приймати й повертати лише серіалізовні значення?** Тому що аргументи й результати перетинають мережеву межу між клієнтом і сервером у вигляді закодованих даних. Функції, екземпляри класів і DOM-вузли не можна серіалізувати, тож їх передавання спричиняє помилку. `FormData`, звичайні об'єкти, масиви й примітиви — безпечні.
**Як працює progressive enhancement із Server Actions?** Коли дію передано напряму в пропс `action` форми, браузер надсилає форму нативно через POST-запит ще до гідратації React. Next.js перехоплює запит і виконує дію на сервері, тож форма працює без клієнтського JavaScript і покращується до клієнтського досвіду після гідратації.
**Яка різниця між викиданням і поверненням помилки в дії?** Викидання винятку поширюється до найближчого error boundary й підходить для неочікуваних збоїв. Повернення структурованого об'єкта стану через `useActionState` підходить для очікуваних помилок валідації, які мають відображатися вбудовано, не розмонтовуючи форму.
**Чи викликає revalidatePath негайне перезавантаження даних?** Ні. Він позначає кешовані записи як застарілі, щоб наступний запит їх регенерував. Поточна відповідь не змінюється, і саме тому перенаправлення після мутації потрапляє на щойно відрендерений вміст.
Для ширшого контексту рендерингу, у якому виконуються ці мутації, розбір [React 19 Server Components у продакшні](/blog/react-next/react-19-server-components-production) природно доповнює знання про Server Actions.
## Висновок
Server Actions у Next.js 16 об'єднують мутації, ревалідацію та progressive enhancement в єдиний серверний примітив. Головні висновки:
- Мутації варто позначати директивою `"use server"` й передавати у пропс `action` форми, щоб безкоштовно отримати обробку мережі та progressive enhancement.
- Станами очікування й валідації керує `useActionState`, що повертає структурований об'єкт для очікуваних помилок замість викидання винятку.
- `revalidatePath` підходить, коли змінюється один маршрут, а `revalidateTag` — коли ті самі дані охоплюють кілька маршрутів.
- `useOptimistic` миттєво відображає очікуваний результат, а React автоматично узгоджує його або відкочує.
- Авторизацію й валідацію слід виконувати всередині тіла кожної дії, бо кожна використана дія — це публічний HTTP-ендпоінт, який middleware сам по собі не захищає.
---
Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack.
HTML version of this page: https://sharpskill.dev/uk/blog/react-next/nextjs-16-server-actions-mutations-revalidation-interview-questions