javascriptreactjsreact-routerlazy-loadingreact-suspense

How to selectively apply lazy loading and suspense to specific routes with React Router?


I want apply lazy loading to specific routes. I want something like this:

import { lazy, Suspense } from "react";
import { Route, Routes } from "react-router-dom";
import NotLazyComponent from "./NotLazyComponent";

const LazyOne = lazy(() => import("./LazyOne"));
const LazyTwo = lazy(() => import("./LazyTwo"));

const App = () => {
  return (
    <Routes>
      <Route path="/not-lazy" element={<NotLazyComponent />} />
      <Suspense fallback={<div>Loading...</div>}>
        <Route path="/lazy-one" element={<LazyOne />} />
        <Route path="/lazy-two" element={<LazyTwo />} />
      </Suspense>
    </Routes>
  );
};

export default App;

but this won't work. What is the correct way to do that?


Solution

  • Only the Route and React.Fragment components are valid children of the Routes component. Suspense needs to be rendered elsewhere.

    You could wrap individual routes in the Suspense component:

    const App = () => {
      return (
        <Routes>
          <Route path="/not-lazy" element={<NotLazyComponent />} />
          <Route
            path="/lazy-one"
            element={(
              <Suspense fallback={<div>Loading...</div>}>
                <LazyOne />
              </Suspense>
            )}
          />
          <Route
            path="/lazy-two"
            element={(
              <Suspense fallback={<div>Loading...</div>}>
                <LazyTwo />
              </Suspense>
            )}
          />
        </Routes>
      );
    };
    

    Create a layout route that wraps the lazily loaded nested route components:

    import { Routes, Route, Outlet } from 'react-router-dom';
    
    const SuspenseLayout = () => (
      <Suspense fallback={<div>Loading...</div>}>
        <Outlet />
      </Suspense>
    );
    
    const App = () => {
      return (
        <Routes>
          <Route path="/not-lazy" element={<NotLazyComponent />} />
          <Route element={<SuspenseLayout />}>
            <Route path="/lazy-one" element={<LazyOne />} />
            <Route path="/lazy-two" element={<LazyTwo />} />
          </Route>
        </Routes>
      );
    };
    

    Or lift the Suspense component higher in the ReactTree outside the Routes component:

    const App = () => {
      return (
        <Suspense fallback={<div>Loading...</div>}>
          <Routes>
            <Route path="/not-lazy" element={<NotLazyComponent />} />
            <Route path="/lazy-one" element={<LazyOne />} />
            <Route path="/lazy-two" element={<LazyTwo />} />
          </Routes>
        </Suspense>
      );
    };