Topic 52 of 54
19 Server Actions
Overview
Historically, to send data from a client form to the server, you had to write a separate API route, manage `onSubmit` events, use `fetch()`, and handle loading states. React 19 Server Actions allow you to define an asynchronous server function and pass it *directly* to the HTML `<form action={...}>` attribute. React handles the RPC (Remote Procedure Call) networking automatically.
Syntax
When the user clicks submit, React automatically bundles the form data, makes a secure POST request to the server, executes the function, and returns.
Defining and Using a Server Action
jsx
// actions.js (This file runs only on the server)
'use server'; // Marks these functions as Server Actions
export async function updateUser(formData) {
// We extract the data directly from the native FormData object
const name = formData.get('name');
// Direct database mutation!
await db.updateUser({ name });
}
// ----------------------------------------------------
// FormComponent.jsx (Client or Server Component)
import { updateUser } from './actions';
export default function ProfileForm() {
return (
// We pass the Server Action directly to the 'action' prop!
// No preventDefault, no fetch, no API routes!
<form action={updateUser}>
<input type="text" name="name" />
<button type="submit">Update</button>
</form>
);
}Common Pitfalls
- Forgetting the `'use server'` directive. Without it, the client will try to execute the database code and crash.
- Not validating the `formData` on the server. Just because it's a Server Action doesn't mean it's secure from malicious input.
Interview Tips
- Server Actions support 'Progressive Enhancement'. If you use them on a form, the form will actually work and submit data *even if JavaScript is disabled* in the user's browser!
Real-World Example
Server Actions are the core data mutation strategy in Next.js App Router.
example
jsx
'use server';
import { revalidatePath } from 'next/cache';
export async function deletePost(postId) {
await db.post.delete({ where: { id: postId } });
// Instantly trigger a re-render of the page to reflect the deletion
revalidatePath('/posts');
}