ErrorCard for page errors, ErrorBoundary for inline, StatusCard for 404/expected states.
Errors are in-between states we hope users never see—but they will. Network failures, database timeouts, validation errors, and unexpected exceptions are inevitable. The question isn't whether errors will occur, but whether users can recover from them gracefully.
Design components should own error presentation and recovery paths, making route files thin wrappers that compose these building blocks.
Create a reusable error display component:
export function ErrorCard({ error, reset, title = 'Something went wrong' }) {
return (
<Card>
<CardHeader>
<AlertCircle className="text-destructive size-12" />
<CardTitle>{title}</CardTitle>
</CardHeader>
<CardContent>
{reset && <Button onClick={reset}>Try again</Button>}
</CardContent>
</Card>
);
}
}The error.tsx route file becomes a thin wrapper:
'use client';
export default function PostError({ error, reset }) {
useTrackError(error); // Log to error tracking service
return (
<ErrorCard
error=The reset function re-renders the error boundary's children, giving the operation another chance.
For errors within a page (not full-page), use an error boundary component:
export function ErrorBoundary({ children, label }) {
return (
<ReactErrorBoundary
fallbackRender={({ error, resetErrorBoundary }) =>
For states like 404 that aren't really "errors," use a neutral status card:
// not-found.tsx
export default function NotFound() {
return (
<StatusCard
icon={FileQuestion}
title="Post Not Found"
The pattern: design components own visuals and recovery UX; route files compose them with minimal logic.
Not all errors need a full error boundary. For Server Function results—form submissions, deletions, toggles—use toasts (Sonner) to give immediate feedback without disrupting the page:
'use client';
import { useActionState } from 'react';
import { toast } from 'sonner';
const [state, formAction] = useActionState(async (The key insight: error boundaries handle unexpected failures (crashes, network errors). Toasts handle expected outcomes (validation errors, success confirmations). Use both together—error boundaries catch what you didn't anticipate, toasts communicate what you did.