Documentation
Documentation

Error Handling Guide

Learn how to implement comprehensive error handling in your Next.js application.

Error Pages

Create custom error pages for different scenarios:

app/not-found.tsxtypescript
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 <Link
10 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 Home
14 </Link>
15 </div>
16 </div>
17 )
18}

Global error handling for unexpected errors:

app/error.tsxtypescript
1'use client'
2
3export default function Error({
4 error,
5 reset,
6}: {
7 error: Error & { digest?: string }
8 reset: () => void
9}) {
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 <button
19 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 again
23 </button>
24 <Link
25 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 Home
29 </Link>
30 </div>
31 </div>
32 </div>
33 )
34}

API Error Handling

Handle API errors consistently across your application:

lib/error.tstypescript
1// Custom error class for API errors
2export class APIError extends Error {
3 constructor(
4 message: string,
5 public status: number,
6 public code?: string
7 ) {
8 super(message)
9 this.name = 'APIError'
10 }
11}
12
13// Error handler for Supabase
14export function handleSupabaseError(error: any): never {
15 if (error.code === 'PGRST301') {
16 throw new APIError('Resource not found', 404, 'NOT_FOUND')
17 }
18
19 if (error.code === 'PGRST204') {
20 throw new APIError('Invalid input', 400, 'INVALID_INPUT')
21 }
22
23 throw new APIError(
24 'An unexpected error occurred',
25 500,
26 'INTERNAL_SERVER_ERROR'
27 )
28}
29
30// Usage in route handlers
31export async function GET(request: Request) {
32 try {
33 const supabase = createRouteHandlerClient({ cookies })
34 const { data, error } = await supabase
35 .from('posts')
36 .select()
37 .single()
38
39 if (error) {
40 handleSupabaseError(error)
41 }
42
43 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 }
51
52 return Response.json(
53 { error: 'Internal Server Error' },
54 { status: 500 }
55 )
56 }
57}

Error Components

Create reusable error components:

components/error.tsxtypescript
1interface ErrorMessageProps {
2 title?: string
3 message?: string
4 action?: {
5 label: string
6 onClick: () => void
7 }
8}
9
10export 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 <button
24 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}
33
34// Error boundary component
35export 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 }
43
44 static getDerivedStateFromError() {
45 return { hasError: true }
46 }
47
48 componentDidCatch(error: Error, errorInfo: ErrorInfo) {
49 console.error('Error caught by boundary:', error, errorInfo)
50 }
51
52 render() {
53 if (this.state.hasError) {
54 return this.props.fallback
55 }
56
57 return this.props.children
58 }
59}

Best Practices

  • Use Error Boundaries: Wrap components that might fail in error boundaries to prevent the entire app from crashing.
  • Consistent Error Handling: Use a consistent approach to handling and displaying errors across your application.
  • Meaningful Error Messages: Provide clear, actionable error messages that help users understand and resolve the issue.
  • Error Logging: Implement proper error logging to track and debug issues in production.
  • Graceful Degradation: Ensure your application continues to function (perhaps with reduced functionality) when errors occur.
  • Type Safety: Use TypeScript to catch potential errors at compile time.