React Router v5.0 Nested Routes

It took me some time to find the answer that I favoured. Both @johnny-peter & @gaurab-kc solutions were great and they tought me about React's routing mechanism.

@johnny-peter 's solution had disadvantage of forcing me to put a prefix for all auth related routes under /auth/... (e.g. /auth/login & auth/sign-up) which I didn't wanted.

@gaurab-kc solution was supporting only one set of routes.. so if user was already signed up, he couldn't visit the /login route anymore.

Up till recently I used my own solution which had the problem of "defeating the whole purpose of a common header and footer." as @johnny-peter mentioned and it was down-voted few times as it should be.

Now I'm using another solution:

<Router history={browserHistory}>
    <Switch>
        <Redirect exact from="/" to="/home"/>
        <Route exact path={["/login", "/sign-up", ...]}>
            <AuthLayout>
                <Switch>
                    <Route
                        path="/login"
                        component={LoginPage}
                    />
                    <Route
                        path="/sign-up"
                        component={SignUpPage}
                    />
                </Switch>
            </AuthLayout>
        </Route>
        <Route exact path={[
            "/home",
            "/dashboard",
            ...
        ]}>
            <SiteLayout>
                <Switch>
                    <Route
                        path="/home"
                        component={HomePage}
                    />
                    <Route
                        path="/dashboard"
                        component={DashboardPage}
                    />
                </Switch>
            </SiteLayout>
        </Route>
        <Route path="*" component={NotFoundPage}/>
    </Switch>
</Router>

which prevents all the above disadventages. It's allows me to:

  1. Use a layout for each section which isn't re-rendered on route change.
  2. Doesn't forcing me to add any prefix to routes.
  3. All routes are working at the same time, letting users to route back into the /login or other auth routes without logout first.

The only disadvantage of this solution is having more code and duplicating the routes, but it's a cost I'm willing to pay.


Each of your layout should have a path component to differentiate from other layouts.

For example

Auth layouts could reside under /auth eg, login would /auth/login, signup would be /auth/signup

App layout could go under /app eg, dashboard would be /app/dashboard, home would be /app/home

Working Demo

Edit hungry-dubinsky-q1l62

App.js

import { Switch, BrowserRouter, Route, Redirect } from "react-router-dom";

function App() {
  return (
    <BrowserRouter>
      <Layouts />
    </BrowserRouter>
  );
}

Layouts.js

const NotFound = () => <h1>Not Found</h1>;

function Layouts() {
  return (
    <Switch>
      <Route path="/auth" component={AuthLayout} />
      <Route path="/app" component={AppLayout} />
      <Route path="/" component={NotFound} />
    </Switch>
  );
}

AuthLayout

const Signup = () => <p>Login</p>;
const Login = () => <p>Sign up</p>;

function AuthLayout() {
  return (
    <div>
      <h1>Auth Layout</h1>
      <Route path="/auth/signup" exact component={Signup} />
      <Route path="/auth/login" exact component={Login} />
      <Redirect from="/auth" to="/auth/login" exact />
    </div>
  );
}

AppLayout

const Home = () => <p>Home</p>;
const Dashboard = () => <p>Dashboard</p>;

function AppLayout() {
  return (
    <div>
      <h1>App Layout</h1>
      <Route path="/app/home" exact component={Home} />
      <Route path="/app/dashboard" exact component={Dashboard} />
      <Redirect from="/app" to="/app/home" exact />
    </div>
  );
}

Also if you want to protect certain routes from being rendered if not authenticated, then you can create a PrivateRoute component that would redirect to auth layout if not authenticated.

PrivateRoute.js

const PrivateRoute = ({ component: Component, ...rest }) => (
  <Route
    {...rest}
    render={props => sessionStorage.token // your auth mechanism goes here
      ? <Component {...props} />
      : <Redirect to={{ pathname: '/auth' }} />}
  />
);

You can use this PrivateRoute component instead of react-router's Route component.

Eg:

<PrivateRoute path="/app" component={AppLayout} />


You could try having two different switch statements to handle your Auth and Protected routes. I had a similar use case at work and having two sets of switch blocks with only one running at one time was the way for me.

const App: React.FC = () => {
    const history = createBrowserHistory();

    return (
        <div className="App">
            <Router history={history}>
                {isLoggedIn ? <PrivateRoutes /> : <AuthRoutes />}
            </Router>
        </div>
    );
};


const PrivateRoutes: React.FC = () => {
    return (
        <>
            <Header />
            <Switch>
                <Route path="/home" component={HomePage} />
                <Route path="/dashboard" component={DashboardPage} />
                <Route path="*" component={NotFoundPage} />
            </Switch>
            <Footer />
        </>
    );
};

const AuthRoutes: React.FC = () => {
    return (
        <>
            <Header />
            <Switch>
                <Route path="/login" component={LoginPage} />
                <Route path="/sign-up" component={SignUpPage} />
                <Route path="*" component={NotFoundPage} />
            </Switch>
            <Footer />
        </>
    );
};