Server Actions & Data Mutations
5 questions foundWhat is a server action in React and what problem does it solve?
Intermediate A server action is a function that runs directly on the server but can be called from a component as if it were a normal function, letting a form submit data and update a database without needing to manually build a separate API endpoint or write client side fetch code.
// A server action, marked with 'use server'
async function createPost(formData) {
'use server';
await db.posts.create({ title: formData.get('title') });
}
function NewPostForm() {
return (
<form action={createPost}>
<input name="title" />
<button type="submit">Create Post</button>
</form>
);
}
Real-world example A blogging platform lets writers publish a new post by submitting a form directly to a server action, skipping the need to build and call a separate API route just to save the post to the database.
Common follow-ups: How does a server action differ from a traditional API route in terms of how you write it?;What security considerations apply when writing a server action?
Server Components;Forms: Controlled & Uncontrolled Components
How does the useFormStatus hook help show loading feedback while a server action is running?
Advanced useFormStatus gives you access to information about the nearest parent form's current submission state, including a pending flag, letting you disable a submit button or show a loading spinner automatically while a server action is still processing, without manually tracking that state yourself.
import { useFormStatus } from 'react-dom';
function SubmitButton() {
const { pending } = useFormStatus();
return (
<button type="submit" disabled={pending}>
{pending ? 'Saving' : 'Save'}
</button>
);
}
Real-world example A comment form automatically disables its submit button and shows a saving message while a server action processes the new comment, using useFormStatus instead of manually managing a separate loading state.
Common follow-ups: Why must useFormStatus be called inside a component nested within the form, rather than the form component itself?;How does this compare to manually tracking a loading state with useState?
Forms: Controlled & Uncontrolled Components;Custom Hooks
How does the useFormState hook, sometimes called useActionState, let you show validation errors returned from a server action?
Advanced This hook connects a form to a server action and also gives you access to the most recent result returned by that action, such as a validation error message, letting you display feedback to the user without manually managing that state separately.
import { useActionState } from 'react';
async function createPost(previousState, formData) {
'use server';
if (!formData.get('title')) return { error: 'Title is required' };
await db.posts.create({ title: formData.get('title') });
return { error: null };
}
function NewPostForm() {
const [state, formAction] = useActionState(createPost, { error: null });
return (
<form action={formAction}>
<input name="title" />
{state.error && <p>{state.error}</p>}
<button type="submit">Create Post</button>
</form>
);
}
Real-world example A signup form shows a clear error message returned directly from its server action when a username is already taken, without needing any separate client side validation state to track that message.
Common follow-ups: What is passed as the previous state argument the very first time the form is submitted?;How does this pattern compare to handling errors with a traditional client side fetch call?
Error Handling;Forms: Controlled & Uncontrolled Components
How would you refresh data shown on a page after a server action successfully changes something in the database?
Intermediate Frameworks that support server actions, like Next.js, provide a function such as revalidatePath, which tells the framework that data for a specific page is now outdated and should be fetched fresh the next time it is requested, ensuring users see the updated information right away.
async function deletePost(id) {
'use server';
await db.posts.delete(id);
revalidatePath('/posts');
}
Real-world example A blog admin panel automatically shows an updated post list immediately after deleting a post, since the server action tells the framework to refresh the posts page's data right after the deletion completes.
Common follow-ups: What is the difference between revalidating a specific path and revalidating a specific data tag?;What happens if you forget to trigger revalidation after a mutation?
Server-Side Rendering (SSR) & Next.js;Data Fetching (React Query & SWR)
What is a simple real world example of using a server action for a common form, like a newsletter signup?
Beginner A newsletter signup form can call a server action directly when submitted, which saves the visitor's email address to a database on the server, without the developer needing to write a separate API route or manual fetch call just for this one simple task.
async function subscribeToNewsletter(formData) {
'use server';
const email = formData.get('email');
await db.subscribers.create({ email });
}
<form action={subscribeToNewsletter}>
<input name="email" type="email" placeholder="Enter your email" />
<button type="submit">Subscribe</button>
</form>
Real-world example A company blog adds a simple newsletter signup form at the bottom of every article, saving each new subscriber's email directly through a server action without building any extra backend code just for this feature.
Common follow-ups: How would you show a thank you message after the visitor successfully subscribes?;What validation should be added to prevent invalid or duplicate email addresses?
Forms: Controlled & Uncontrolled Components;Email & Notifications