Xiphe / remix-island

utils to render remix into a dom-node instead of the whole document
MIT License
131 stars 7 forks source link

Error Boundary not reached on the server side #13

Open alancoppin opened 10 months ago

alancoppin commented 10 months ago

I have implemented remix-island to solve hydration issues related to extension modifying the DOM. But having an issue when an error is thrown in the Head component, on the server side, the error boundary isn't reached!

image (2)

This obviously gives a flash of the application and then the Error Boundary for the user https://github.com/Xiphe/remix-island/assets/22746959/95622388-3549-43cc-aa6e-88d51bf51701

I have a SafeHead when an error is thrown on the server side, SafeHead being a Head component with only safe elements that don't have dynamic content.

try {
  head = renderHeadToString({ request, remixContext, Head });
 } catch (error) {
  head = renderHeadToString({ request, remixContext, Head: SafeHead });
 }

It might be a pitfall of remix-island but it's not ideal. Anyone has had the issue and found a workaround?

Xiphe commented 10 months ago

Hi @alancoppin thanks for bringing this up!

As far as I understand this happens because we're calling the renderHeadToString outside of remix domain.

There might be a solution where we'd emulate some of remixes error-handling behavior in renderHeadToString and maybe even communicate back to remix using remixContext but I think that's outside of the scope of this library and your approach seems like a pretty straight forward solution to me.

That said when anyone would like to take a shot at solving this, I'm open to PRs

SimeonC commented 1 month ago

I'd found this same issue and ended up not using remix-island because of this issue, but I thought as I'd found a simple workaround I'd come back and share it (even though my company has decided to not use remix long term).

function renderShell({ remixContext }: { remixContext: EntryContext }) {
  const headString = renderToString(<RemixHeader context={remixContext} url={request.url} />);
}

function RemixHeader({ context, url }: { context: EntryContext; url: string }) {
  return (
    <RemixServer
      context={{
        ...context,
        routeModules: {
          ...context.routeModules,
          root: { // changing this seems to allow us to just pass everything down as we remove the `<Outlet />` and override the "ErrorBoundary"
            default: () => (
              <>
                <Meta />
                <Links />
              </>
            ),
            ErrorBoundary: () => (
              <>
                <Meta />
                <Links />
              </>
            ),
          },
        },
      }}
      url={url}
      abortDelay={ABORT_DELAY}
    />
  );
}
Xiphe commented 1 week ago

Thanks for sharing @SimeonC <3