react-router
react-router copied to clipboard
[Bug]: lazy loaded module error in nested router is catched only by the parent route with the * wildcard, not by the `errorElement` in the nested router
What version of React Router are you using?
6.22.3
Steps to Reproduce
I use microfrontend architecture in my app. I have a RouteProvider
in my host app with the createBrowserRouter
router routes which have an *
wildcard in their path
:
host-app.router.ts
const Settings = lazy(() => import('microfrontend_settings/App'));
export const HostRouter = () => {
const router = createBrowserRouter([
{
path: '/settings/*',
element: <Settings />,
errorElement: <HostErrorLayout />,
},
],
{
basename: '/',
});
return (
<RouterProvider router={router} />
);
};
In my remote Settings app I also have a routing but I cannot use RouterProvider
there because there must be only one router provider per whole app. Also, in my remote Settings app I cannot use createBrowserRouter
router, because such a router needs its own provider, but as I mentioned previously, I cannot use nested router providers.
So in remote Settings app I use just <Routes>
(without <BrowserRouter>
component):
remote-settings-app.router.ts
const Notifications = lazy(() => import('microfrontend_settings_notifications/App'));
const Transactions = lazy(() => import('microfrontend_settings_transactions/App'));
export const RemoteRouter = () => {
return (
<Routes>
<Route
element={
<SuspenseLayout fallback={SkeletonLayout}>
<DefaultLayout />
</SuspenseLayout>
}
errorElement={<RemoteErrorLayout />}
>
<Route
path="notifications"
element={<Notifications />}
/>
...
<Route
path="transactions"
element={<Transactions />}
/>
</Routes>
);
};
So, my RemoteRouter is made with the <Routes>
and is located inside the HostRouter
which is made with the createBrowserRouter
.
In order to remote Settings app router work I use a *
wildcard in my host app /settings/*
route — now my microfrontend app is displaying on /settings/notifications
page correctly.
I made my Transactions app broken by purpose — remoteEntry.js
file just does not exist on the server, so lazy loaded module throws an 404 error: Failed to fetch dynamically imported module.
So, the problem is now if I navigate to the /settings/transactions
page the error will be displayed in my HostErrorLayout
component — my RemoteErrorLayout
in RemoteRouter
will be just skipped and not catching an error.
Expected Behavior
Error bubbling should respect every route tree errorElement
on any route tree level.
Actual Behavior
Error bubbling respects only root route's errorElement
if the route has *
wildcard.
I have tried to put an <Outlet />
component in my host app errorElement
component, but it seems error element just does not respect a router outlet.
So, there is no any difference what level deep is nested my microfrontend routing — now I have Host app → remote Settings app → remote Integrations app → remote API app, all of the remotes are nested and have its own <Routes>
inside and all of them have an errorElement
.
So, when I break in purpose my remote API app I expect either Integrations app or Settings app will catch bubbled error but this does not happen — error bubbles to the main root router.