#
Isolate module failures
One of the key characteristics of micro-frontends implementations like iframes and subdomains is the ability to isolate failures within individual modules, preventing them from breaking the entire application.
However, with a Module Federation implementation, this is not the case as all the modules share the same browsing context (e.g. the same Document object, the same Window object, and the same DOM). A failure in one module can potentially breaks the entire application.
Nevertheless, an application, federated or non-federated, can get very close to iframes failure isolation by utilizing React Router's Outlet component and the errorElement property of a React Router's routes.
#
Create an error boundary
First, define a React Router's error boundary to catch module errors. For this example we'll name it ModuleErrorBoundary
:
export function ModuleErrorBoundary() {
return (
<div>An error occured while rendering a page from a module!</div>
)
}
#
Register the error boundary
Then, update the host application registerHost
function to declare the ModuleErrorBoundary
component below the RootLayout
component but above the routes of the modules. By doing so, if a module encounters an unhandled error, the error boundary will only replace the section rendered by the Outlet
component within the root layout rather than the entire page.
A React Router's error boundary is declared with the errorElement of a route:
import { PublicRoutes, ProtectedRoutes, type ModuleRegisterFunction, type FireflyRuntime } from "@squide/firefly";
import { RootLayout } from "./RootLayout.tsx";
import { ModuleErrorBoundary } from "./ModuleErrorBoundary.tsx";
export const registerHost: ModuleRegisterFunction<FireflyRuntime> = runtime => {
runtime.registerRoute({
element: <RootLayout />,
children: [
{
// Error boundary for modules.
errorElement: <ModuleErrorBoundary />,
children: [
PublicRoutes,
ProtectedRoutes
]
}
]
}, {
hoist: true
});
};
By implementing this mechanism, the level of failure isolation achieved is comparable to that of an iframes or subdomains implementation. With this mechanism, failure isolation is as good as with an iframes or subdomains implementation.
#
Hoisted pages
If your application is hoisting pages, it's important to note that they will be rendered outside of the host application's ModuleErrorBoundary
component. To prevent breaking the entire application when an hoisted page encounters unhandled errors, it is highly recommended to declare a React Router's error boundary for each hoisted page as well, again using errorElement:
import { type ModuleRegisterFunction, type FireflyRuntime } from "@squide/firefly";
import { Page } from "./Page.tsx";
import { RemoteErrorBoundary } from "./RemoteErrorBoundary.tsx";
export const register: ModuleRegisterFunction<FireflyRuntime> = runtime => {
runtime.registerRoute({
path: "remote/page",
element: <Page />,
errorElement: <RemoteErrorBoundary />
}, {
hoist: true
});
};
#
Try it 🚀
Start the application in a development environment using the dev
script. Update any of your application routes that is rendered under the newly created error boundary (e.g. that is not hoisted) and throw an Error
. The error should be handled by the ModuleErrorBoundary
component instead of breaking the whole application.
#
Troubleshoot issues
If you are experiencing issues with this guide:
- Open the DevTools console. You'll find a log entry for each registration that occurs and error messages if something went wrong.
- Refer to a working example on GitHub.
- Refer to the troubleshooting page.