Error Handling (Actions)
Gracefully handling failures without blowing up the UI.
To Throw or Not To Throw
When a Next.js Server Action encounters an uncaught error, it automatically triggers the nearest React Error Boundary (error.js).
While this is great for catastrophic system failures, it is terrible for expected user errors (like a taken username or an invalid credit card), because it destroys the user's form UI.
Inline Feedback
To provide a good user experience, you want the form to stay exactly where it is, preserving their typed input, while displaying a small red error message above the submit button.
To achieve this, the Server Action must *catch* the error internally and return it as a normal object.
The Safe Return Pattern
Wrap your server logic in a standard try/catch block.
If the operation succeeds, return { success: true }. If it fails, catch the error and return { error: 'Human readable message' }.
The client component can then check if (res.error) and set a local React state to display the message inline.
The Safe Return
Hover over the code lines to see how returning a standard JavaScript object prevents the dreaded Error Boundary from wiping out the user's form.
action.ts (Server)
import { redirect } from 'next/navigation';
export async function saveData(formData) {
await db.save(formData);
return { error: "Save failed" };
redirect('/success');
page.tsx (Client)
export default function Form() {
async function onSubmit(formData) {
setError(res.error);
return (
Check yourself
Pick an answer to lock it in, then read why. Getting one wrong is part of how it sticks.
Remember this
- Uncaught errors in Server Actions trigger
error.jsboundaries. - For form validation, use
try/catchto return safe{ error: string }objects. - Never return native
Errorobjects to the client; they don't serialize. - Never put
redirect()inside atry/catchblock, or it will be swallowed. - Client components use the returned error string to update inline UI state.
Done with this concept?
Mark it complete to track your progress. No login needed.