Define React Router Routes In Another Components

Viewed 918

I am using react-router-dom 5.2.0. I want 2 files that define all of my routes. 1 file for public routes, 1 file for private. The right component renders when I hit the home route, but not the /content route.

App.jsx returns:

<BrowserRouter>
  <Switch>
    <PublicRoutes />
    <PrivateRoutes />
  </Switch>
</BrowserRouter>

PublicRoutes.jsx returns:

<Route path="/" exact render={() => <h1>home route</h1>} />

And PrivateRoutes.jsx returns:

<Route path="/content" exact render={() => <h1>Content Route</h1>} />

I am able to load / with "home route", but nothing renders when I hit the /content url.

3 Answers

Those Switches need to go inside your wrapper routes, like so:

import { BrowserRouter, Switch, Route } from "react-router-dom";
import "./styles.css";

const PublicRoutes = () => (
  <Switch>
    <Route path="/" exact render={() => <h1>home route</h1>} />
  </Switch>
);
const PrivateRoutes = () => (
  <Switch>
    <Route path="/content" exact>
      <h1>Content Route</h1>
    </Route>
  </Switch>
);

export default function App() {
  return (
    <BrowserRouter>
      <PublicRoutes />
      <PrivateRoutes />
    </BrowserRouter>
  );
}

https://codesandbox.io/s/shy-river-ysv4w?file=/src/App.js

Issue

The Switch component returns and renders the first matching Route or Redirect component. PublicRoutes nor PrivateRoutes are neither, the PublicRoutes is returned by default.

Switch

Renders the first child <Route> or <Redirect> that matches the location.

Solution

You'll need to render a Route in the Switch that checks the authentication condition and conditionally render one or the other.

<BrowserRouter>
  <Switch>
    <Route
      render={(routeProps) => authCondition ? (
        <PublicRoutes {...routeProps} />
      ) : (
        <PrivateRoutes {...routeProps} />
      )}
    />
  </Switch>
</BrowserRouter>

If your public and private routes are split on the path then you can likely more simply render each on their perspective paths.

<BrowserRouter>
  <Switch>
    <Route path="/content" component={PrivateRoutes} />
    <Route path="/" component={PublicRoutes} />
  </Switch>
</BrowserRouter>

With either you will then likely need to render a nested Switch that builds on the base path of each if you've further nested routes.

in react router v6 it is possible by using outlet base on docs

or you should use this

function App() {
  return (
    <Routes>
      <Route path="/" element={<Home />} />
      <Route path="dashboard/*" element={<Dashboard />} />
    </Routes>
  );
}

function Dashboard() {
  return (
    <div>
      <p>Look, more routes!</p>
      <Routes>
        <Route path="/" element={<DashboardGraphs />} />
        <Route path="invoices" element={<InvoiceList />} />
      </Routes>
    </div>
  );
}

please Pay attention to * end of path="dashboard/*"

Related