Description
I'm currently setting up global error handling by wrapping the <Router /> with a catch-all <ErrorBoundary> (e.g., from react-error-boundary). However, once this outermost error boundary catches an error, navigating away (e.g., clicking the browser's back button) does not clear the error state.
The Context & Pain Point
While route-level errors can be handled by placing an <ErrorBoundary> inside a root Layout component to wrap the <Outlet />, this approach cannot catch errors occurring within the Layout itself or fatal errors during router initialization.
To handle these unhandled exceptions as a last resort, we must place a catch-all boundary outside the <Router>.
Since Funstack Router lacks built-in error handling props, developers naturally use an <ErrorBoundary> outside the router for this safety net. However, being outside the router context means we cannot use hooks like useLocation to reset the boundary.
While we can manually solve this by subscribing to the Navigation API and using NavigationHistoryEntry.key as the reset key, developers migrating from other routing libraries might not realize this pattern, leaving users permanently stuck on the fallback UI.
Proposed Solutions
To improve the Developer Experience regarding global error handling, I suggest the following options:
- Provide an Official Recipe in the Docs
Add a section explaining how to properly handle global errors using the Navigation API and useSyncExternalStore. For example:
import { useSyncExternalStore } from 'react';
import { Router } from '@funstack/router';
import { ErrorBoundary } from 'react-error-boundary';
const subscribe = (callback: () => void) => {
if (typeof window === 'undefined' || !window.navigation) return () => {};
window.navigation.addEventListener('currententrychange', callback);
return () => window.navigation.removeEventListener('currententrychange', callback);
};
const getSnapshot = () => window.navigation?.currentEntry?.key || '';
const getServerSnapshot = () => '';
function useNavigationKey() {
return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
}
export default function App() {
const navKey = useNavigationKey();
return (
<ErrorBoundary resetKeys={[navKey]} FallbackComponent={GlobalErrorFallback}>
<Router routes={routes} />
</ErrorBoundary>
);
}
-
Export a Helper Component
Provide a <GlobalErrorBoundary> wrapper that hooks into the Navigation API internally to automatically reset on location change.
-
Add an errorComponent property to the route API
Allow defining an error fallback directly in the route configuration.
const routes = [
route({
path: '/',
component: Layout,
errorComponent: GlobalErrorFallback, // Automatically resets on navigation
children: [ ... ]
})
];
I'd love to hear your thoughts on these options. If there is already a better way or a recommended best practice that I might have missed, please let me know!
Description
I'm currently setting up global error handling by wrapping the
<Router />with a catch-all<ErrorBoundary>(e.g., fromreact-error-boundary). However, once this outermost error boundary catches an error, navigating away (e.g., clicking the browser's back button) does not clear the error state.The Context & Pain Point
While route-level errors can be handled by placing an
<ErrorBoundary>inside a rootLayoutcomponent to wrap the<Outlet />, this approach cannot catch errors occurring within theLayoutitself or fatal errors during router initialization.To handle these unhandled exceptions as a last resort, we must place a catch-all boundary outside the
<Router>.Since Funstack Router lacks built-in error handling props, developers naturally use an
<ErrorBoundary>outside the router for this safety net. However, being outside the router context means we cannot use hooks likeuseLocationto reset the boundary.While we can manually solve this by subscribing to the Navigation API and using
NavigationHistoryEntry.keyas the reset key, developers migrating from other routing libraries might not realize this pattern, leaving users permanently stuck on the fallback UI.Proposed Solutions
To improve the Developer Experience regarding global error handling, I suggest the following options:
Add a section explaining how to properly handle global errors using the Navigation API and
useSyncExternalStore. For example:Export a Helper Component
Provide a
<GlobalErrorBoundary>wrapper that hooks into the Navigation API internally to automatically reset on location change.Add an
errorComponentproperty to therouteAPIAllow defining an error fallback directly in the
routeconfiguration.I'd love to hear your thoughts on these options. If there is already a better way or a recommended best practice that I might have missed, please let me know!