I am trying to lazy load a component in React. It works if I declare the component at the top of the module. However it does not render anything if I declare it in a inline way.
Case 1 : Works as expected
import React, { lazy, Suspense } from "react";
import { Routes, Link, Route, Outlet } from "react-router-dom";
import CaseStudies from "./pages/CaseStudies";
const ExampleAssembly = lazy(() => import("./pages/ExampleAssembly"));
export default function App() {
return (
<>
<Suspense fallback={<p>Loading...</p>}>
<Routes>
<Route path="/casestudies" element={<CaseStudies />} >
<Route path="assembly" element={<ExampleAssembly />} />
</Route>
</Routes>
</Suspense>
</>
);
}
Case 2: Doesn’t work
import React, { lazy, Suspense } from "react";
import { Routes, Link, Route, Outlet } from "react-router-dom";
import CaseStudies from "./pages/CaseStudies";
export default function App() {
return (
<>
<Suspense fallback={<p>Loading...</p>}>
<Routes>
<Route path="/casestudies" element={<CaseStudies />} >
<Route path="assembly" element={ lazy(() => {
return import("./pages/ExampleAssembly");
}) } />
</Route>
</Routes>
</Suspense>
</>
);
}
Is there a correct way to write inline lazy loaded components? Or should I always have lazy loaded component definitions at the top as in Case1?
2
Answers
Always do it like how you have it in case 1. You’re trying to force a pattern that doesn’t, and shouldn’t, exist in the other example.
Short answer: you should always load your component definitions at the top as in Case 1.
lazy
is not designed to be used in the inline fashion that you describe. As the React docs say:So, it’s failing in Case 2 because you’re using
lazy
‘s return value as a function, rather than as a component, which is why React logs an errorWarning: Functions are not valid as a React child
.You could use the return value with
createElement
, in which case it does actually render:This is, however, creating a new lazy component every time the
Route
is rendered. It’s essentially the same as declaring a new component in a function scope, which is a definite anti-pattern, except it also involves a Promise that does network loading on every render. Just … don’t do it.