Introduction

There are two fundamentally different error types. I call them the soft and the hard error, since one is gonna break the application totally and the other only does unintended things, that aren’t actually breaking anything. In this article I will talk about the differences and how to handle each of them.


Expected Exceptions (Soft)

This error can be expected because it’s part of the normal application flow. They are handled explicit in code with conditional rendering, redirect or notFound components. Common examples could be:

  • Missing data or routes
  • Invalid input
  • Lack of authorization

Handling expected exceptions

Te best way to deal with that issue is to make use of Next.js not-found.tsx component which is usually placed within the route like:

src/app/project/[projectId]/not-found.tsx

Now I only need to create my component, here I’m gonna make use of my own πŸ“œ Placeholder Component to display a custom message:

import Link from "next/link";
import { Placeholder } from "@/components/placeholder";
import { Button } from "@/components/ui/button";
import { ticketsPath } from "@/paths";
 
export default function NotFound() {
  return (
    <Placeholder
      label="We could not find your ticket"
      button={
        <Button asChild variant="outline">
          <Link href={ticketsPath()}>Go to Tickets</Link>
        </Button>
      }
    />
  );
}
 

I then can easily call this component whenever an excepted exception occurs, like trying to fetch an missing object from a database.

const TicketPage = async ({ params }: TicketPageProps) => {
 
  if (!ticket) {
    notFound(); // Show not-found.tsx
  }
 
  return (
  //...
  );
};
 
export default TicketPage;

Uncaught exceptions (hard)

This errors are unexpected runtime failures that are most likely gonna break the application. They are should be handled by a error.tsx file wich signal that something serious went wrong. Common examples could be:

  • Bugs
  • System issues
  • Failed network/database operations
  • Accessing undefined values

Handling uncaught exceptions

The best way to deal with this errors, is to make use of Next.js error.tsx file. When placing the error.tsx file next to the page.tsx file, Next.js will display it’s content to the user and also avoiding the app to crash fully.

It’s important to mention that these error.tsx file will only work for their subtree. If I want a file that works for the whole application, I should make use of the global-error.tsx file.

src/app/project/error.tsx

And again I will make use of the πŸ“œ Placeholder Component to display the error message to the user.

"use client";
 
import { Placeholder } from "@/components/placeholder";
 
export default function Error({ error }: { error: Error }) {
  return <Placeholder label={error.message ?? "Something went wrong!"} />;
}

ErrorBoundary

The error.tsx file will replace the whole page.tsx component. If I want more control, I can use the ErrorBoundary to replace only some part of my component. It’s the same principe like with Loading Components and Suspense.

npm install react-error-boundary

The ErrorBoundy kinda acts as a try .. catch. It will try to display its children, but if there happens to be an error, it will then display the fallback component. Other components of the same page won’t be replaced, unlike with the error.tsx file.

<ErrorBoundary fallback={<Placeholder label="Something went wrong!" />}>
  <TicketList />
</ErrorBoundary>

Conclusion

Errors are always a pain but unfortunately unavoidable. Thankfully Next.js and React offer good solutions for each use case. Hopefully with knowing that, my application will never crash in production - I dream that most likely won’t come true.