How to handle errors gracefully in Remix
Tags: react, remix, javascript, webdev
Published: 2021-12-16 03:31:49

## Using ErrorBoundary and CatchBoundary in error handling

### How Remix handles errors

> **From the Remix official documentation**
>
> Remix will automatically catch errors and render the nearest error boundary for errors thrown while:
>
> - rendering in the browser
> - rendering on the server
> - in a loader during the initial server rendered document request
> - in an action during the initial server rendered document request
> - in a loader during a client-side transition in the browser (Remix serializes the error and sends it over the network to the browser)
> - in an action during a client-side transition in the browser

### What can we use to handle errors in Remix?

Since Remix is still a JavasScript framework, we can still use JavaScript constructs to catch errors such as `try/cath`.
This blog will only discuss how to catch errors using `ErrorBoundary` and `CatchBoundary`.

<br />

#### Using `ErrorBoundary`

> **From the official Docs**
>
> An ErrorBoundary is a React component that renders whenever there is an error anywhere on the route, either during rendering or during data loading.

Here is an example of using the `ErrorBoundary` component to catch errors in Remix.

```tsx
import type { ErrorBoundaryComponent } from "remix";

export const ErrorBoundary: ErrorBoundaryComponent = ({ error }) => {
  return <div>ERROR: {error.message}</div>;
};
```

Using the `ErrorBoundary` above, Here are some examples of errors we can catch and the corresponding result.

<br />

##### - Explicitly thrown errors
```tsx
export function loader() {
  throw new Error('I am a failure!')
}
```

```html
<div>ERROR: "I am a failure!"</div>
```

##### - Unhandled errors

```tsx
export function action() {
	return fetch("http://a.b.c");
}
```

```html
<div>ERROR: "request to http://a.b.c/ failed, reason: getaddrinfo ENOTFOUND a.b.c"</div>
```

##### - Other errors in backend
```tsx
export function loader() {
  // syntax error
  someCodeInBackend;
}
```

```html
<div>ERROR: "someCodeInBackend is not defined"</div>
```

##### - Errors in React land

```tsx
export default function Index() {
  someCodeInReact;

  return <div>{/*...*/}</div>;
}
```

```html
<div>ERROR: "someCodeInReact is not defined"</div>
```

<br />

#### Using `CatchBoundary`

> A CatchBoundary is a React component that renders whenever an action or loader throws a Response.

It works in combination with `useCatch`. The hook will allow us to get a reference to the response's `data`, `status`, and `statusText`.


```tsx
export function CatchBoundary() {
  const caught = useCatch();

	return (
    <>
      <div>
        ERROR: {caught.statusText} {caught.status}
      </div>
      <div>{caught.data.message}</div>
    </>
  );
}
```

```tsx
export function loader() {
  throw json({ message: "Some custom message" }, 501);
}
```

will yield to 

```html
<div>ERROR: Not Implemented 501</div>
<div>Some custom message</div>
```

We can also use other data formats such as plain string, but we need to make sure our `CatchBoundary` can handle them properly.

```tsx
export function action() {
  throw json("Some custom message", 501);
}
```

will yield to 

```html
<div>ERROR: Not Implemented 501</div>
<div></div>
```

<br />

### In which cases should we use one over the other?

We should use `CatchBoundary` if we care about the context of the error, such as status codes or the custom data related to the error; in another case, we use `ErrorBoundary`.

The good thing is we can even use them together. So, for example, if there's an error in the `CatchBoundary`, that error will be caught by the `ErrorBoundary`.

<br />

### Live examples

#### Handling global 404 error
[/non-exisiting-page](https://playground-efupcps19-codegino.vercel.app/non-exisiting-page)

#### Handling route 404 error
[/words/404](https://playground-efupcps19-codegino.vercel.app/words/404)

#### Handling error in an action

Try to fill in the form, then click submit to see the `401` error.

[/words/add](https://playground-efupcps19-codegino.vercel.app/words/add)

#### Handing error in a nested route
[/words/edit/404](https://playground-efupcps19-codegino.vercel.app/words/edit/404)

#### Propagating error from `/test` to the root route
[/test](https://playground-efupcps19-codegino.vercel.app/test)

<br />

### Additional note:

> Your root `ErrorBoundary` should not throw an error in a production environment since there's nothing else to catch the error. If the root `ErrorBoundary` can throw an error, the user might see something like [this](https://playground-jb6w7fn95-codegino.vercel.app/words/404)


## Conclusion

I find using `ErrorBoundary` and `CatchBoundary` an easy way to handle route errors in Remix as it allows us to have fine-grained control on how we want to display errors in our pages. In my opinion, it improves the readability of what we are trying to achieve as there is the precise placement of the success and error cases.