React Router - How can I reuse my layout for the errorElement in the root route?
Asked Answered
B

2

11

I'm using React Router v6 and following are my routes:

const router = createBrowserRouter([
  {
    path: '/',
    element: <App />,
    errorElement: <ErrorPage />,
    children: [
      {
        index: true,
        element: <HomePage />,
      },
      {
        path: '/sign-up',
        element: <SignUpPage />,
      },
      {
        path: '/log-in',
        element: <LogInPage />,
      },
    ],
  },
]);

const root = ReactDOM.createRoot(
  document.getElementById('root') as HTMLElement,
);

root.render(
  <React.StrictMode>
    <RouterProvider router={router} />
  </React.StrictMode>,
);

The App component contains my app's layout and outputs the route elements using the Outlet component. But now if there's an error that bubbles up to the root route, then the ErrorPage gets displayed as expected, but it doesn't make use of the layout from App... So, how can I reuse my layout from App when the error page gets displayed?

Bedpan answered 1/6, 2023 at 19:13 Comment(0)
A
16

When there's an error it is kind of an either/or kind of scenario. Either the conditions are fine, and the App component is rendered or there's an error condition, and the ErrorPage component is rendered.

What you could do is abstract the layout portion of the App component into a layout component on its own that can render either a passed children prop or the Outlet component for the nested route, and render it in App and also wrap the ErrorPage component.

Example:

const AppLayout = ({ children }) => (
  ...
  {children ?? <Outlet />}
  ...
);
const App = () => (
  ...
  <AppLayout />
  ...
);
const router = createBrowserRouter([
  {
    path: "/",
    element: <App />, // <-- uses Outlet
    errorElement: (
      <AppLayout>     // <-- uses children
        <ErrorPage />
      </AppLayout>
    ),
    children: [
      {
        index: true,
        element: <HomePage />
      },
      {
        path: "/sign-up",
        element: <SignUpPage />
      },
      {
        path: "/log-in",
        element: <LogInPage />
      }
    ]
  }
]);

Edit react-router-how-can-i-reuse-my-layout-for-the-errorelement-in-the-root-route

Aesir answered 1/6, 2023 at 19:45 Comment(4)
But in this case <AppLayout /> component will be re-created between navigation? No?Otto
@RomanMahotskyi It might, depending on where AppLayout is in the ReactTree. React and React-Router-DOM try to keep components mounted as much as possible so the extra work of unmounting and remounting the same component is avoided. Is there a specific issue you have because of my answer here?Aesir
No issues; it was only mentioned that <AppLayout /> may re-render, which could result in the loss of state.Otto
@RomanMahotskyi Sure, this is basically just a bare-bones minimal implementation example. If there is any state that needs to "persist" you can always lift it higher in the ReactTree to a stable location if there is an problem with the layout remounting.Aesir
D
1
const router = createBrowserRouter([
  {
    path: '/',
    element: <App />,
    children: [
      {
        index: true,
        element: <HomePage />,
      },
      {
        path: '/sign-up',
        element: <SignUpPage />,
      },
      {
        path: '/log-in',
        element: <LogInPage />,
      },
    ].map(route => ({ errorElement: <ErrorPage />, ...route })),
  },
]);
Dube answered 27/6, 2024 at 9:46 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.