Forms And Server Actions
Handle mutations and form submissions with Server Actions, validation, and progressive enhancement.
Why Server Actions Help
Server Actions are async functions marked with `"use server"` that run on the server and are callable from forms and client components. They eliminate boilerplate API routes for CRUD mutations colocated with UI.
Forms submit FormData directly to actions — works without JavaScript via progressive enhancement. The browser posts to the server; Next.js invokes the action function.
- Colocated server mutations
- No separate API route boilerplate
- Progressive enhancement with native forms
'use server';
export async function createPost(formData: FormData) {
const title = formData.get('title');
await db.post.create({ data: { title: String(title) } });
}Validation And Feedback
Validate FormData with Zod on the server — never trust client validation alone. Return structured errors `{ fieldErrors: { email: 'Invalid' } }` for client display via useActionState.
Use redirect() after successful mutation to implement POST-redirect-GET pattern, preventing duplicate submissions on refresh.
- Server-side Zod validation
- Structured field errors to client
- redirect after successful mutation
useActionState and Pending States
useActionState (formerly useFormState) wraps actions with state tracking — previous return value, pending boolean. Pass to form action prop for automatic submission handling.
Disable submit buttons while pending is true. Show inline field errors from returned state. useFormStatus (in child components) reads pending state from parent form context.
- useActionState for action return state
- pending flag for submit UX
- useFormStatus in submit button children
'use client';
const [state, action, pending] = useActionState(createPost, null);
<form action={action}>...</form>Revalidation After Mutations
Call `revalidatePath('/posts')` or `revalidateTag('posts')` inside actions after data changes so cached pages reflect new data. Without revalidation, static and ISR pages serve stale content until their revalidate interval.
revalidatePath refreshes specific routes; revalidateTag invalidates all fetches tagged during data retrieval. Choose based on whether cache keys are path-centric or data-centric.
- revalidatePath after mutations
- revalidateTag for tagged fetch cache
- Required for stale static content update
Security Considerations
Verify authentication and authorization inside every Server Action — they are public endpoints callable by anyone who can reach your server. CSRF protection is built into Next.js Server Actions via origin header validation.
Rate limit sensitive actions. Never expose internal IDs without ownership checks. Log mutation attempts for audit trails on financial or admin operations.
- Auth check in every action
- Built-in CSRF protection
- Rate limit sensitive mutations