Protected Routes in React Router v6
Two simple ways to set up private routes in your react project
Table of contents
Introduction
The new React Router v6 update brought a lot of changes, while also stripping away some functionalities. One of these changes we will focus on is how you set up protected or private routes. For those who do not know, a private route (also called a protected route) is a route/component in an application that is only available to a user after a specific condition is met. For example, a user's dashboard is only accessible to the user if he/she is logged in.
Private Routes In v5
The previous version of react-router allowed you to nest components and also use a function called Redirect
to redirect users based on a given condition. With this, the common way of setting up private routes is as follows:
<switch>
<Route exact path='/'>
{!user && <Redirect to='/login' />}
{user && <Dashboard />}
</Route>
<Route path='/login'>
{user && <Redirect to='/' />}
{!user && <Login/>}
</Route>
</switch>
Notice that all routes are inside the switch
component and the base URL must include an exact
prop if not it would be considered a match to any URL entered by the user and thus would always be displayed no matter the path. Inside the first route component, we check if we have a user (assuming the user
variable represents a user object); if it is false we redirect the user to the login route, otherwise we render the homepage. The purpose of the second route definition is to make sure that a user is not allowed to access the login page if he/she is already logged in.
Private Routes In v6
One of the major changes with version 6 is that the switch
component has been replaced with a Routes
component, and the exact
prop has been removed as the router automatically matches only one route with a complete match. The new update also requires that all children of the routes
component must be individual routes. Another change is that Redirect
has been replaced with Navigate
, which works a bit differently.
Using Outlet
The new update also added another component called Outlet
which you can use to create a child route. However, for this to work, you need to create a separate component in which you'll conditionally pass an Outlet
to a component, then add the component as an element parameter to a route. We're going to call this route PrivateRoutes
in case we might want to use it multiple times (you can use any name of your choice):
import { Navigate, Outlet } from 'react-router-dom'
const PrivateRoutes = () => {
const { user } = AuthContext()
return (
user ? <Outlet/> : <Navigate to='/login'/>
)
}
Now we can import and use this component in our routes as follows:
<Routes>
<Route element={<PrivateRoutes/>}>
<Route path='/' element={<Home/>} />
</Route>
<Route path='/login' element={<Login/>}/>
</Routes>
This method is particularly useful when the same rule applies to multiple routes (e.g. the home and product page which you might want to make accessible only when the user is logged in). One disadvantage of this method as you might have observed is that if we want to set two different conditions as we did we the example on version 5, we would have to create two different components to handle those conditions. This leads to more code and can also be confusing at times; that is why I like to use this second method of achieving this.
Using The Ternary Operator
This method is very straightforward and simple:
<Routes>
<Route element={user ? <Home /> : <Login />} path="/" />
<Route element={!user ? <Login /> : <Home />} path="/login" />
</Routes>
With this method, when the user goes to the base URL, we check if they are already logged in. If we have a user object (meaning the user is logged in), we render the homepage; else we render the login page for the user to log in. The same condition applies to the second route.
The Caveat
One thing you might have noticed from the example above is that, while we passed two different components to be rendered alternatively, the path is still the same, which means that whichever component is being rendered, the path (the URL displayed by the browser) is still the same. Thus if the login page was rendered, the browser still displays the base URL meant for the homepage and likewise for the second route. I'm not aware of any security risks imposed by this (if you do please share in the comments section below). However, some people might not be comfortable with this, if you're one of those people, I would advise you to use the first method.
Conclusion
There are many ways to kill a rat!
The above quote is a statement I heard from a friend a few years back which still rings in my mind to this day because of how it applies to most of the things we do each day. When it comes to programming, it's really hard to state which method is the absolute best; it is mostly based on preferences. I'm sure the two methods I shared above are not the only ways of setting up private routes, it's left for you to decide which method works for you.
Thanks for reading!