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:
- Use a layout for each section which isn't re-rendered on route change.
- Doesn't forcing me to add any prefix to routes.
- All routes are working at the same time, letting users to route back into the
/login
or otherauth
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
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 />
</>
);
};