Search code examples
reactjsreact-router-dom

How to use react-router-dom nested routes using outlet in separate file


Here is the link to sandbox: https://codesandbox.io/s/react-router-dom-nested-routes-z9hy45

I'm using react-router-dom and define my routes in separate file, I want to use nested routes with outlet for protected routes

<Routes>
    <Route path="/" element={<About />} />
    <Route path="/login" element={<Login />} />

    <Route element={<ProtectedLayout />}>
      <Route path="/user" element={<User />} />
    </Route>
</Routes>

This works exactly how I want, but I want to declare this logic in routes.js file and just use

const routes = useRoutes(publicRoutes)
const protectedRoutes = useRoutes(privateRoutes)
return (
  <div>
    {router}
    // So what I want is to protectedRoutes be wrapped by ProtectedLayout somehow in routes.js
    {protectedRoutes}
  </div>

Solution

  • Map the public/private routes to JSX, and import & render these in App.

    Example:

    routes.js

    import { Route } from "react-router-dom";
    import { Home } from "./components/Home";
    import { Login } from "./components/Login";
    import { User } from "./components/User";
    
    export const publicRoutes = [
      {
        path: "/",
        element: <Home />
      },
      {
        path: "/login",
        element: <Login />
      }
    ].map((props) => <Route {...props} />);
    
    export const privateRoutes = [
      {
        path: "/user",
        element: <User />
      }
    ].map((props) => <Route {...props} />);
    

    App.js

    import "./styles.css";
    import { Route, Routes } from "react-router-dom";
    import { ProtectedLayout } from "./components/ProtectedLayout";
    import { privateRoutes, publicRoutes } from "./routes";
    
    export default function App() {
      return (
        <div className="App">
          <Routes>
            {publicRoutes}
            <Route element={<ProtectedLayout />}>
              {privateRoutes}
            </Route>
          </Routes>
        </div>
      );
    }
    

    You could also instead use a Data router and pass the routes in the configuration object.

    routes.js

    import { Home } from "./components/Home";
    import { Login } from "./components/Login";
    import { User } from "./components/User";
    
    export const publicRoutes = [
      {
        path: "/",
        element: <Home />
      },
      {
        path: "/login",
        element: <Login />
      }
    ];
    
    export const privateRoutes = [
      {
        path: "/user",
        element: <User />
      }
    ];
    

    App.js

    import "./styles.css";
    import { createBrowserRouter, RouterProvider } from "react-router-dom";
    import { ProtectedLayout } from "./components/ProtectedLayout";
    import { privateRoutes, publicRoutes } from "./routes";
    
    const router = createBrowserRouter([
      ...publicRoutes,
      {
        element: <ProtectedLayout />,
        children: [...privateRoutes]
      }
    ])
    
    export default function App() {
      return (
        <div className="App">
          <RouterProvider router={router} />
        </div>
      );
    }