Using react-router v6
and react v18
. I am not sure what I am missing, but it seems like the nesting component with lazy does not work.
I have this file structure:
routes/root.tsx
routes/profiles/Profiles.tsx
routes/profiles/ProfileDetail.tsx
routes/profiles/ProfilesList.tsx
Now in the root, I have a router defined like this:
createBrowserRouter([
{
path: "/",
element: <AuthLayout />,
children: [
{
path: "/profiles/*",
lazy: () => import("./profiles/Profiles.tsx"),
},
],
},
])
And then in the Prfiles.tsx
:
export const Component = () => {
// some code
return useRoutes([
{
path: "/",
lazy: () => import("./ProfilesList"),
},
{
path: "/:id",
lazy: () => import("./ProfileDetail"),
},
])
}
Both ProfilesList.tsx
and ProfileDetail.tsx
export a Component
.
The AuthLayout
gets rendered, but when I go to /profiles
or /profiles/someId
it does not ender anything. It just shows empty page. (with a menu that is contained in the AuthLayout
. The Component in Profiles.tsx
does get rendered, but the profile list and detail does not.
Am I missing something? I dont see a reason why this should not work.
I tried to not lazy load the Profiles.tsx
component in the root router config. If instead of using lazy I use element inside the Profiles.tsx
component for the routes. It works as expected, but when I use tha lazy option it does not work.
Since the lazy function just get a function with the file and uses its Component,loader etc... I would suspect that it would work the same way as with other routes like the Profile.tsx
itself where exporting a Component
and than lazy
in the createBrowserRouter
does work.
it seems like the nesting component with lazy does not work.
It is my opinion that the official docs might be a bit misleading when it comes to lazily loading components using the Route
component's lazy
prop when they include this note:
This feature only works if using a data router, see Picking a Router
It seems the caveat to this is that the lazy
only works when literally used within the createBrowserRouter
declaration. Since the "/profiles"
route renders descendent routes it is difficult (or impossible) for the Data router to know what routes will be rendered and available at runtime, so the Profiles
component can't use React-Router's lazy loading solution.
You'll need to do all the routing/loading in where the router is created. The Profiles
component should be converted to a layout route component, e.g. it renders an Outlet
for the nested routes instead of directly rendering the descendent routes manually.
Example:
Profiles.tsx
import { Outlet } from 'react-router-dom';
export const Component = () => {
// some code
return (
...
<Outlet />
...
);
}
const router = createBrowserRouter([
{
path: "/",
element: <AuthLayout />,
children: [
{
path: "/profiles",
lazy: () => import("./profiles/Profiles.tsx"),
children: [
{
index: true,
lazy: () => import("./profiles/ProfilesList.tsx"),
},
{
path: ":id",
lazy: () => import("./profiles/ProfileDetail.tsx"),
},
],
},
],
},
]);
Alternatively you can use the regular React lazy import solution if you stil want to code split and use descendent routes.
Example:
Both ProfilesList
and ProfileDetail
should be default exported
const Component = () => {
...
};
export default Component;
Profiles.tsx
import { lazy, Suspense } from "react";
import { useRoutes } from "react-router-dom";
const ProfilesList = lazy(() => import("./ProfilesList"));
const ProfileDetail = lazy(() => import("./ProfileDetail"));
export const Component = () => {
// some code
const profilesRoutes = useRoutes([
{
path: "/",
element: <ProfilesList />,
},
{
path: "/:id",
element: <ProfileDetail />,
},
]);
return (
<>
<h1>Profiles</h1>
<Suspense fallback={<h1>Loading...</h1>}>{profilesRoutes}</Suspense>
</>
);
};