Jak używać Server Actions w Next.js 16 bez magicznego myślenia? Formularze, useActionState, walidacja, bezpieczeństwo i momenty, w których Route Handlers nadal są lepsze.
Server Actions to jedna z najmocniejszych funkcji App Router, ale też jedna z najczęściej upraszczanych w tutorialach. Pozwalają wykonywać kod na serwerze bezpośrednio z komponentów, często bez pisania osobnych endpointów API, czyli Application Programming Interface, definiuje sposób komunikacji między aplikacjami lub modułami. dla mutacji w obrębie tej samej aplikacji.
Brzmi jak magia? Tylko do momentu, w którym wchodzą walidacja, autoryzacja, rewalidacja cache i współpraca z klientem. Jeśli dopiero zaczynasz z Next.js, sprawdź najpierw jak skonfigurować projekt od zera. W tym artykule pokażę, jak Server Actions działają, kiedy ich używać i gdzie kończy się wygoda, a zaczynają realne trade-offy.
Krótka odpowiedź: Server Actions to asynchroniczne funkcje wykonywane na serwerze, które możesz wywoływać bezpośrednio z komponentów React bez pisania osobnych endpointów API. Pozwalają obsługiwać formularze, mutacje danych i walidację po stronie serwera z wbudowaną rewalidacją cache i progressive enhancement. Warto ich używać do standardowych operacji CRUD to skrót od Create, Read, Update, Delete, czyli podstawowych operacji wykonywanych na danych. wewnątrz jednej aplikacji Next.js, ale nie zastępują Route Handlers tam, gdzie potrzebujesz publicznego API lub pełnej kontroli nad HTTP.
Server Actions to funkcje asynchroniczne wykonywane na serwerze, które możesz wywoływać bezpośrednio z komponentów React — zarówno Server Components, jak i Client Components.
useTransition pozwala śledzić stan ładowania bez blokowania UI, czyli User Interface, to wizualna i interakcyjna warstwa produktu..
useActionState — stan formularza
W aktualnym React i Next.js do obsługi wyniku akcji najczęściej użyjesz useActionState. Starsze materiały często pokazują useFormState, ale dziś lepiej trzymać się nowszego API:
Po mutacji często chcesz odświeżyć dane na stronie:
Code
// app/actions.ts'use server'import { revalidatePath, revalidateTag } from 'next/cache'export async function createPost(formData: FormData) { const title = formData.get('title') as string await db.post.create({ data: { title } }) // Odśwież konkretną ścieżkę revalidatePath('/posts') // Lub odśwież po tagu revalidateTag('posts', 'max')}
Jeśli wywołujesz mutację w Server Action i chcesz natychmiast zobaczyć własny zapis, sprawdź też updateTag(). revalidateTag(..., 'max') działa bardziej w modelu stale-while-revalidate.
Przekierowania
Code
// app/actions.ts'use server'import { redirect } from 'next/navigation'export async function createPost(formData: FormData) { const post = await db.post.create({ data: { title: formData.get('title') as string }, }) redirect(`/posts/${post.id}`)}
redirect musi być wywołany poza blokiem try/catch — rzuca specjalny error, który Next.js przechwytuje.
Optymistyczne aktualizacje
Dla lepszego UX, czyli User Experience, opisuje całe doświadczenie użytkownika podczas korzystania z produktu. możesz aktualizować UI przed zakończeniem akcji:
Code
'use client'import { useOptimistic } from 'react'import { likePost } from '@/app/actions'export function LikeButton({ postId, initialLikes }: { postId: string initialLikes: number}) { const [optimisticLikes, addOptimisticLike] = useOptimistic( initialLikes, (state, increment: number) => state + increment ) const handleLike = async () => { addOptimisticLike(1) // natychmiast +1 await likePost(postId) // potem prawdziwa akcja } return ( <button onClick={handleLike}> ❤️ {optimisticLikes} </button> )}
Jeśli akcja się nie powiedzie, React automatycznie cofnie optymistyczną zmianę.
Progressive Enhancement
Formularze z Server Actions działają nawet bez JavaScript:
Bez JS: formularz wysyła tradycyjny POST, serwer przetwarza i przekierowuje.
Z JS: Next.js przechwytuje submit, wykonuje akcję asynchronicznie, aktualizuje UI.
Bezpieczeństwo
Server Actions są bezpieczne, ale pamiętaj o podstawach:
1. Zawsze waliduj dane
Code
// ❌ Źle — brak walidacjiexport async function updateUser(formData: FormData) { 'use server' await db.user.update({ where: { id: formData.get('id') }, data: { role: formData.get('role') }, // użytkownik może ustawić role: 'admin'! })}// ✅ Dobrze — walidacja + autoryzacjaexport async function updateUser(formData: FormData) { 'use server' const session = await getSession() if (!session?.user) throw new Error('Unauthorized') const data = UserUpdateSchema.parse({ name: formData.get('name'), // nie przyjmuj 'role' z formularza! }) await db.user.update({ where: { id: session.user.id }, // używaj ID z sesji, nie z formularza data, })}
2. Formularz w UI nie jest autoryzacją
To, że przycisk renderujesz tylko zalogowanemu użytkownikowi, nie oznacza jeszcze bezpieczeństwa. Server Action musi sama weryfikować sesję i uprawnienia, bo request może zostać odtworzony poza normalnym flow interfejsu.
3. Sprawdzaj uprawnienia
Code
export async function deletePost(postId: string) { 'use server' const session = await getSession() if (!session?.user) throw new Error('Unauthorized') const post = await db.post.findUnique({ where: { id: postId } }) if (post?.authorId !== session.user.id) { throw new Error('Forbidden') } await db.post.delete({ where: { id: postId } })}
import { Ratelimit } from '@upstash/ratelimit'import { Redis } from '@upstash/redis'import { headers } from 'next/headers'const ratelimit = new Ratelimit({ redis: Redis.fromEnv(), limiter: Ratelimit.slidingWindow(5, '1 m'), // 5 żądań na minutę})export async function submitForm(formData: FormData) { 'use server' const ip = (await headers()).get('x-forwarded-for') ?? 'anonymous' const { success } = await ratelimit.limit(ip) if (!success) { return { error: 'Zbyt wiele żądań. Spróbuj później.' } } // ... reszta logiki}
Kiedy używać Server Actions?
Używaj Server Actions dla:
Formularzy (kontakt, logowanie, rejestracja)
Mutacji danych (CRUD)
Prostych akcji (like, follow, delete)
Gdy chcesz progressive enhancement
Rozważ API Routes gdy:
Potrzebujesz endpointu dla zewnętrznych klientów
Integrujesz z webhookami
Potrzebujesz pełnej kontroli nad HTTP (headers, status codes)
Budujesz publiczne API
Chcesz oddzielić transport HTTP od logiki domenowej
FAQ
Czym różnią się Server Actions od Route Handlers w Next.js?
Server Actions to funkcje serwerowe wywoływane bezpośrednio z komponentów React — głównie do mutacji danych i obsługi formularzy wewnątrz jednej aplikacji. Route Handlers (dawniej API Routes) tworzą pełnoprawne endpointy HTTP z kontrolą nad nagłówkami, statusami i metodami — odpowiednie dla publicznych API, webhooków i klientów zewnętrznych.
Czy Server Actions są bezpieczne?
Tak, ale bezpieczeństwo trzeba aktywnie zapewnić. Każda Server Action musi samodzielnie weryfikować sesję i uprawnienia, walidować dane wejściowe (np. przez Zod) oraz chronić przed nadużyciami przez rate limiting. Sam fakt, że przycisk jest widoczny tylko dla zalogowanego użytkownika, nie zabezpiecza akcji.
Jak obsłużyć stan ładowania i błędy formularza w Server Actions?
Używaj hooka useActionState z React do śledzenia stanu akcji i zwracania komunikatów o błędach. Do obsługi stanu przycisku submit podczas wysyłania formularza służy useFormStatus z react-dom. Oba hooki działają razem i pozwalają budować responsywne formularze bez osobnego zarządzania stanem.
Czy formularze z Server Actions działają bez JavaScript?
Tak — to jedna z kluczowych zalet. Gdy JavaScript nie jest dostępny, formularz wysyła tradycyjny POST, a serwer przetwarza dane i wykonuje przekierowanie. Gdy JS jest aktywny, Next.js przechwytuje submit i wykonuje akcję asynchronicznie bez pełnego przeładowania strony (progressive enhancement).
Jak rewalidować cache po Server Action?
Po wykonaniu mutacji wywołaj revalidatePath('/sciezka'), żeby odświeżyć konkretną stronę, albo revalidateTag('tag', 'max'), żeby unieważnić wszystkie zasoby z danym tagiem cache w modelu stale-while-revalidate. Gdy chcesz natychmiast odczytać świeży wynik po własnej mutacji, rozważ też updateTag(). Funkcje importujesz z next/cache i wywołujesz bezpośrednio wewnątrz Server Action.
Czy można używać Server Actions w Client Components?
Tak. Importujesz Server Action z pliku z dyrektywą 'use server' i wywołujesz ją w handlerze lub wewnątrz useTransition. Hook useTransition pozwala śledzić, czy akcja jest w toku, bez blokowania interfejsu użytkownika.
Kiedy Server Actions nie są dobrym wyborem?
Gdy potrzebujesz endpointu dla zewnętrznych klientów (inne aplikacje, mobile), integrujesz webhookowy callback, potrzebujesz pełnej kontroli nad odpowiedzią HTTP (nagłówki, kody statusu) lub budujesz publiczne API konsumowane przez wiele frontendów — w tych przypadkach lepszym wyborem są Route Handlers.
Podsumowanie
Server Actions mocno upraszczają formularze i mutacje w Next.js, ale nie są "magicznie lepsze" w każdej sytuacji:
Brak boilerplate'u API routes
Automatyczna serializacja FormData
Wbudowana rewalidacja cache
Progressive enhancement out of the box
Typowanie end-to-end z TypeScript
To nie zastępuje Route Handlers ani publicznych endpointów HTTP. Dla większości formularzy i mutacji wewnątrz jednej aplikacji są jednak prostszym punktem startu niż ręczne budowanie warstwy API od zera.
Jeśli chcesz przełożyć ten temat na lepszą architekturę frontendu, uporządkować React lub Next.js i podnieść jakość pracy zespołu, skontaktuj się ze mną. Pomagam zamieniać wiedzę z artykułów w praktyczne decyzje technologiczne.
Maciej Sala — project manager i frontendowiec z doświadczeniem w marketingu internetowym. Na co dzień pracuję z Reactem, Next.js i TypeScriptem, łącząc perspektywę produktową z praktycznym podejściem do kodu. Przez kilka lat związany z branżą gier wideo jako project manager i game designer.
Absolwent historii na Uniwersytecie Jagiellońskim i studiów podyplomowych z marketingu internetowego na Akademii Górniczo-Hutniczej w Krakowie. Poza pracą trenuje na siłowni, maluje figurki i realizuje własne projekty.
Anthropic uderza w Figmę i Adobe — oto Claude Design
Anthropic wypuścił właśnie narzędzie AI do tworzenia stron, landing page'ów i prezentacji z promptu. Oto co wiemy o Claude Design i Opus 4.7 — i co to oznacza dla developerów.
Astro.js vs Next.js — które narzędzie wybrać w 2026 roku?
Fachowe porównanie Astro.js i Next.js z perspektywy developera pracującego na co dzień w Next.js. Architektura, wydajność, SEO, DX, koszty i konkretne use case — z benchmarkami i przykładami kodu.
WordPress → Next.js — migracja treści, redirecty 301 i zachowanie pozycji SEO
Jak przenieść stronę z WordPress na Next.js bez utraty pozycji w Google? Eksport treści, mapowanie URL, redirecty 301, migracja obrazów i weryfikacja indeksacji.