Learn how to implement comprehensive error handling in your Next.js application.
Create custom error pages for different scenarios:
1export default function NotFound() {2 return (3 <div className="flex min-h-screen flex-col items-center justify-center">4 <div className="mx-auto max-w-xl text-center">5 <h1 className="mb-4 text-4xl font-bold">404 - Page Not Found</h1>6 <p className="mb-8 text-gray-600">7 The page you're looking for doesn't exist or has been moved.8 </p>9 <Link10 href="/"11 className="rounded-lg bg-gray-900 px-4 py-2 text-sm font-medium text-white transition-colors hover:bg-gray-800"12 >13 Return Home14 </Link>15 </div>16 </div>17 )18}
Global error handling for unexpected errors:
1'use client'23export default function Error({4 error,5 reset,6}: {7 error: Error & { digest?: string }8 reset: () => void9}) {10 return (11 <div className="flex min-h-screen flex-col items-center justify-center">12 <div className="mx-auto max-w-xl text-center">13 <h1 className="mb-4 text-4xl font-bold">Something went wrong!</h1>14 <p className="mb-8 text-gray-600">15 {error.message || 'An unexpected error occurred.'}16 </p>17 <div className="flex justify-center gap-4">18 <button19 onClick={reset}20 className="rounded-lg bg-gray-900 px-4 py-2 text-sm font-medium text-white transition-colors hover:bg-gray-800"21 >22 Try again23 </button>24 <Link25 href="/"26 className="rounded-lg border border-gray-200 px-4 py-2 text-sm font-medium transition-colors hover:bg-gray-50"27 >28 Return Home29 </Link>30 </div>31 </div>32 </div>33 )34}
Handle API errors consistently across your application:
1// Custom error class for API errors2export class APIError extends Error {3 constructor(4 message: string,5 public status: number,6 public code?: string7 ) {8 super(message)9 this.name = 'APIError'10 }11}1213// Error handler for Supabase14export function handleSupabaseError(error: any): never {15 if (error.code === 'PGRST301') {16 throw new APIError('Resource not found', 404, 'NOT_FOUND')17 }1819 if (error.code === 'PGRST204') {20 throw new APIError('Invalid input', 400, 'INVALID_INPUT')21 }2223 throw new APIError(24 'An unexpected error occurred',25 500,26 'INTERNAL_SERVER_ERROR'27 )28}2930// Usage in route handlers31export async function GET(request: Request) {32 try {33 const supabase = createRouteHandlerClient({ cookies })34 const { data, error } = await supabase35 .from('posts')36 .select()37 .single()3839 if (error) {40 handleSupabaseError(error)41 }4243 return Response.json(data)44 } catch (error) {45 if (error instanceof APIError) {46 return Response.json(47 { error: error.message },48 { status: error.status }49 )50 }5152 return Response.json(53 { error: 'Internal Server Error' },54 { status: 500 }55 )56 }57}
Create reusable error components:
1interface ErrorMessageProps {2 title?: string3 message?: string4 action?: {5 label: string6 onClick: () => void7 }8}910export function ErrorMessage({11 title = 'Error',12 message = 'Something went wrong',13 action,14}: ErrorMessageProps) {15 return (16 <div className="rounded-lg border border-red-100 bg-red-50 p-4">17 <div className="flex items-center gap-3">18 <XCircle className="h-5 w-5 text-red-600" />19 <h3 className="font-medium text-red-900">{title}</h3>20 </div>21 <p className="mt-2 text-sm text-red-700">{message}</p>22 {action && (23 <button24 onClick={action.onClick}25 className="mt-3 text-sm font-medium text-red-600 hover:text-red-500"26 >27 {action.label}28 </button>29 )}30 </div>31 )32}3334// Error boundary component35export class ErrorBoundary extends React.Component<36 { children: ReactNode; fallback: ReactNode },37 { hasError: boolean }38> {39 constructor(props: { children: ReactNode; fallback: ReactNode }) {40 super(props)41 this.state = { hasError: false }42 }4344 static getDerivedStateFromError() {45 return { hasError: true }46 }4748 componentDidCatch(error: Error, errorInfo: ErrorInfo) {49 console.error('Error caught by boundary:', error, errorInfo)50 }5152 render() {53 if (this.state.hasError) {54 return this.props.fallback55 }5657 return this.props.children58 }59}