Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How do I provide a custom error boundary for my entry app's entry point (/app) and child routes (app/foo, app/foo/baz)? #597

Closed
muchisx opened this issue Mar 8, 2024 · 5 comments

Comments

@muchisx
Copy link

muchisx commented Mar 8, 2024

// Shopify needs Remix to catch some thrown responses, so that their headers are included in the response.
export function ErrorBoundary() {
return boundary.error(useRouteError());
}

This is very confusing for me and I haven't gotten around to understand can I also return a component in my routes that also don't mess up what you guys intend the error boundary to do, which is to use that boundary import from shopify.

I went here in the docs but I left even more confused. I did not grasp how to do it and there's no examples.

I'm asking because currently without error boundaries my app just brakes whenever I have an error server-side and I have to restart my local server every time.

If someone could explain me like I'm 5 haha, would be great, thanks !

@muchisx muchisx changed the title How do I provide a custom error boundry for my entry app's entry point (/app) and child routes (app/foo, app/foo/baz)? How do I provide a custom error boundary for my entry app's entry point (/app) and child routes (app/foo, app/foo/baz)? Mar 8, 2024
@paulomarg
Copy link
Contributor

Hey! I hear you, error boundaries are tricky right now - we're looking at ways to improve them, but for now we need to do that.

We need that error boundary to be in app.tsx because there are flows where we need to throw responses with headers, so we need to catch those throws at this level for the headers function to kick in.

boundary.error will do 2 things (code for context):

  • If a Response was thrown, we'll return some HTML to render the page normally so the headers are included. This behaviour needs to stay, or the app won't authenticate properly.
  • If an Error was thrown, we'll just rethrow it. As far as our code goes, we don't care what happens after this.

All that aside, if you just want to add some custom code when errors happen, you can do this:

export const ErrorBoundary = () => {
  const error = useRouteError();
  try {
    // Catch redirect Response thrown by @shopify/shopify-app-remix
    const shopifyError = boundary.error(error);
    return shopifyError;
  } catch {
    // Run your own code here
    return <p>Something went wrong ☹️</p>;
  }
};

We'll see if we can add some documentation on how to add your own boundaries, but it will basically be the above :)

Hope this helps!

Copy link

We are closing this issue because we did not hear back regarding additional details we needed to resolve this issue. If the issue persists and you are able to provide the missing clarification we need, you can respond here or create a new issue.

We appreciate your understanding as we try to manage our number of open issues.

@nboliver-ventureweb
Copy link

@paulomarg What is the recommended to process to throw a 404 response from a loader, as per the Remix docs? https://remix.run/docs/hi/main/guides/not-found#how-to-send-a-404

@nboliver-ventureweb
Copy link

From what I can tell so far, this works for returning a custom error UI when a 4xx response is thrown.

export function ErrorBoundary() {
  const error = useRouteError();
  if (isRouteErrorResponse(error)) {
    return (
      <div>
        <h1>
          {error.status} {error.statusText}
        </h1>
        <p>{error.data}</p>
      </div>
    );
  } else {
    try {
      // Catch redirect Response thrown by @shopify/shopify-app-remix
      const shopifyError = boundary.error(error);
      return shopifyError;
    } catch {
      // Run your own code here
      return <p>Something went wrong ☹️</p>;
    }
  }
}

However it seems like it might contradict what is mentioned above:

If a Response was thrown, we'll return some HTML to render the page normally so the headers are included. This behaviour needs to stay, or the app won't authenticate properly.

@paulomarg can you please clarify if my example above is acceptable for retaining the required ErrorBoundary functionality?

@umutnacak
Copy link

umutnacak commented Feb 5, 2025

@nboliver-ventureweb I thought using something similar but after looking at the shopify-app-remix code I understand that the boundary.error function is used for the bounce page implementation. They return a script tag which handles refreshing the session token. So I ended up with something like this:

I send the error like this (I used data utility, but should work with Response and serializing the data):

throw data({ id: "my-unique-id", statusText: "Not Found" }, {
  status: 404,
});

And catch it like this:

if (isRouteErrorResponse(error) && error.data && error.data.id !== "my-unique-id") {
  return (
    <div
      dangerouslySetInnerHTML={{ __html: error.data }}
    />
  );
}
else {
  return <div>Known error or no data handler</div>
}

It's not perfect because I had to use their implementation for any other data to make sure that I don't break anything (if not already). But I think this is an upgrade over the infamous 'Handling response' message.

Basically they need to do this check inside the errorBoundary function of the shopify-app-remix package and re-throw instead of showing the 'Handling response' message.

And as for the internal not found error you can use the ErrorBoundary of the root.tsx like shown in this documentation:
https://remix.run/docs/en/main/file-conventions/root#layout-export which shouldn't require the usage of the boundary functions, but I'm not that sure.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants