When getServerSideProps throws an error, Next.js by default shows a 500 error page in production, but you can implement custom error handling with try-catch blocks, return notFound, or use custom error pages for graceful degradation
When an unhandled error occurs in getServerSideProps, Next.js's default behavior depends on the environment. In development, you'll see an error overlay with detailed debugging information. In production, Next.js shows a generic 500 Internal Server Error page to the user (customizable via pages/500.js or app/global-error.js). The request fails, no page is rendered, and the error is logged to the server console. However, this default behavior often provides poor user experience—a broken page instead of partial content or a graceful fallback. Proper error handling in getServerSideProps is essential for building resilient applications that can recover from API failures, database timeouts, or authentication issues.
Try-catch with fallback props: Catch errors and return partial data or empty states with a flag indicating an error occurred.
Return notFound: When data is missing (404 case), return { notFound: true } to show the custom 404 page.
Return redirect: For authorization failures, redirect to login or an error page.
Custom error page: Use pages/_error.js or app/global-error.js to create branded 500 pages.
Error boundary: In the App Router, wrap client components in error boundaries to handle rendering errors separately.
Next.js allows you to create custom error pages that match your brand. In the Pages Router, pages/_error.js handles both 404 and 500 errors. In the App Router, app/global-error.js specifically handles errors in the root layout (must be a client component), and app/error.js handles errors in nested routes. These error pages can include branding, support links, and automatic retry mechanisms. Unlike the default error page, custom error pages maintain your site's look and feel during failures.
Centralized logging: Integrate services like Sentry, LogRocket, or DataDog to capture errors from getServerSideProps with context (URL, params, headers).
Retry logic: Implement exponential backoff retries for transient failures (network timeouts, database deadlocks).
Partial data rendering: Return what data you can fetch successfully, with placeholders for failed sections.
Circuit breakers: For external API failures, temporarily serve stale cached data instead of failing completely.
Error categorization: Distinguish between client errors (4xx) and server errors (5xx) to show appropriate UI.
In the App Router, the pattern changes because there's no getServerSideProps. Instead, you use async Server Components with try-catch blocks. Error handling is done via error.js files that create error boundaries around route segments. For data fetching errors, you can catch them and either render fallback UI or throw to the nearest error boundary. The App Router also provides notFound() and redirect() functions that can be called directly from Server Components.