# 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, in 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 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 an error boundary to catch module errors. For this example we'll name it RootErrorBoundary:

host/src/RootErrorBoundary.tsx
export function RootErrorBoundary() {
    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 RootErrorBoundary component below the RootLayout 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 RootLayout rather than the entire page:

host/src/register.tsx
import { ManagedRoutes, type ModuleRegisterFunction, type FireflyRuntime } from "@squide/firefly";
import { RootLayout } from "./RootLayout.tsx";
import { RootErrorBoundary } from "./RootErrorBoundary.tsx";

export const registerHost: ModuleRegisterFunction<FireflyRuntime> = runtime => {
    runtime.registerRoute({
        element: <RootLayout />,
        children: [
            {
                // Default error boundary.
                errorElement: <RootErrorBoundary />,
                children: [
                    ManagedRoutes
                ]
            }
        ]
    }, {
        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 root error boundary. To prevent breaking the entire application when an hoisted page encounters unhandled errors, it is highly recommended to declare a React Router's errorElement property for each hoisted page:

remote-module/src/register.tsx
import { ManagedRoutes, 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 error boundary 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.