Avoid bad user experience by exploring different ways to manage runtime errors with Next.js
Intro
If you are working with Next.js, there is quite a good chance to bump into internal server errors (5xx) when on production. If not handled correctly, a single error can bring down the complete page. Imagine a user shopping online for their favourite gadget and they end up seeing this page resulting in a bad UX.
Nextjs shipped SSG support with version 9.3 and brought in a lot of positives. For instance: getting errors at build time rather than runtime. Switching from SSR to SSG, ended up in an amazing user experience as everything was statically generated before the site was deployed.
However, in some cases, we still require our website pages to use server side rendering (SSR) instead of static site generation (SSG). Example: checking if the user is logged in or not?
Plot
In this article, let’s take a look at a typical error, the “TypeError“
Consider our web-application consuming data from a CMS. At some point the marketing team tries to change a property and they accidentally end up removing one. Or, for the sake of this article let’s consider the CMS backend server goes offline. We fail fetching the data from the CMS and the TypeError is born.
This example considers a scenario when your webpage uses Server side rendering.
The source code is spun out of a simple next.js boilerplate, Deployed on https://ssr-error-handling-git-main.meetdave3.vercel.app and available on Github: https://github.com/meetdave3/ssr-error-handling
Let’s take a look of ways how can we avoid a webpage from crashing in production?
1. Error boundary
function Tile(props) {
return (
<ErrorBoundary FallbackComponent={ErrorFallback} key={props.id}>
<a key={props.id} href={props.link} className={styles.card}>
<h3>{props.error.title} →</h3>
<p>{props.para.para.para}</p>
</a>
</ErrorBoundary>
);
}
Since we’re using React, we are aware of using error boundaries as React exposes getDerivedStateFromError or componentDidCatch lifecycle methods so we can handle the runtime errors.
These lifecycle method won’t run in Next.js as componentDidCatch does not work when using SSR
If an error occurs in the Error Boundary, the webpage will simply throw a internal server error (500) and result in an errored page.
So if you are using error boundaries to handle runtime errors & if an error occurs on production, the page will render like so: https://ssr-error-handling-git-main.meetdave3.vercel.app/error-boundary
You will see a 500 internal server error. Yes, it’s annoying and we don’t want our end users to see it either.
2. Try ... catch
When server side rendering, Our old friend Try … catch is a good replacement to the error boundary as it works expectedly on server side, helps us avoiding the annoying 500 internal server error.
You can wrap your risky component with a try catch like so
function Tile(props) {
try {
return (
<a key={props.id} href={props.link} className={styles.card}>
<h3>{props.title} →</h3>
<p>{props.para.para.para}</p>
</a>
);
} catch (e) {
// Send to an error monitoring solution or log it.
return null;
}
}
Check: https://ssr-error-handling-git-main.meetdave3.vercel.app/try-catch
and you can see how the complete page doesn’t crash any more
3. Optional Chaining
function Tile(props) {
return (
<a href={props?.link} className={styles.card}>
<h3>{props?.error?.title} →</h3>
<p>{props?.para?.para?.para}</p>
</a>
);
}
This method is the best case from all the options when we want to solve a TypeError. It’s minimal, it’s fast, still it can only help us in accessing chained properties without throwing any error.
Conclusion
It’s not necessary to use any of these methods if you are statically generating the site (SSG)
Why? Because we will get the TypeError at build time when running next build
and a production build won’t be created.
When using SSR, we need to fallback to the try .. catch solution when we are trying to do something more error prone like calculating the total tax of the shopping basket when rendering the checkout page.
It’s best to use a mix of optional chaining and try catch to avoid server side errors. Avoid falling into the trap of using React error boundaries when creating a server side rendered page.
Top comments (1)
I would think you'd want a HOC or wrapper component for the try-catch solution - I'm very unaccustomed to seeing render data wrapped in try catch.