I am using React v17, and React Router v6+. When I load the page, the browser downloads all the js which is around 900kb, reducing the initial load time.
My routes are defined like so
const PrivateRoute = lazy(() => import("../utils/AuthenticatedRoutes"));
const Profile = lazy(() => import("../modules/Settings/User/Profile"));
const Buddies = lazy(() => import("../modules/Buddies/Buddies"));
const Buddy = lazy(() => import("../modules/Buddies/Buddy"));
const App = () => {
return (
<Suspense fallback={<Loader />}>
<Routes>
<Route path="/" element={<Landing />} />
<Route path="/profile" element={<PrivateRoute render={<Profile />} />} />
<Route path="/buddies" element={<PrivateRoute render={<Buddy />} />} />
</Routes>
</Suspense>
)
}
This is the Private Route component
const PrivateRoute = ({ render }: { render: any }) => {
const location = useLocation();
const { loggedIn } = useSelector((state: RootState) => state.userReducer);
const pathname = location.pathname;
if (!loggedIn) {
return <Navigate to={`/login?redirectTo=${pathname}&search=${location.search}`} />;
}
return render;
};
Problem: When I load any page on the app, even the one with no element in it, the entire JS of 999kb is downloaded and I don't think lazy loading should work that way.
How can I handle this ?
I don't see any overt issues with the way you've implemented your code. The code splitting appears to be working.
I suggest refactoring the PrivateRoute component to be a layout route component instead of a wrapper component.
Example:
import { Navigate, Outlet, useLocation } from 'react-router-dom';
const PrivateRoute = () => {
const location = useLocation();
const { loggedIn } = useSelector((state: RootState) => state.userReducer);
const pathname = location.pathname;
if (!loggedIn) {
return <Navigate to={`/login?redirectTo=${pathname}&search=${location.search}`} />;
}
return <Outlet />;
};
...
const PrivateRoute = lazy(() => import("../utils/AuthenticatedRoutes"));
const Profile = lazy(() => import("../modules/Settings/User/Profile"));
const Buddies = lazy(() => import("../modules/Buddies/Buddies"));
const Buddy = lazy(() => import("../modules/Buddies/Buddy"));
const App = () => {
return (
<Suspense fallback={<Loader />}>
<Routes>
<Route path="/" element={<Landing />} />
<Route element={<PrivateRoute />}>
<Route path="/profile" element={<Profile />} />
<Route path="/buddies" element={<Buddy />} />
</Route>
</Routes>
</Suspense>
)
}
With this implementation I do see the Loader component "blip" on the screen momentarily for the first time each dynamically imported component is routed to.
I think it might be as Colin called out in a comment, that the dynamically imported components just aren't a significant portion of your overall app bundle size.
This is normal. You are wrapping the whole app with Suspense, which is directly resolved by your fallback while anything under it is suspended.
Suspense with react router ?You should defined Suspense for each route you want to have lazy loading. So when the route is called, suspense will call the fallback while anything under it is suspended.
const PrivateRoute = lazy(() => import("../utils/AuthenticatedRoutes"));
const Profile = lazy(() => import("../modules/Settings/User/Profile"));
const Buddies = lazy(() => import("../modules/Buddies/Buddies"));
const Buddy = lazy(() => import("../modules/Buddies/Buddy"));
const App = () => {
return (
<Routes>
<Route path="/" element={<Landing />} />
<Route path="/profile" element={<PrivateRoute render={<React.Suspense fallback={<Loader />}><Profile /></React.Suspense>} />} />
<Route path="/buddies" element={<PrivateRoute render={<React.Suspense fallback={<Loader />}><Buddy /></React.Suspense>} />} />
</Routes>
)
}
This is the same as the official documentation