Creating Protected Routes With React Router V6

Dennis Ivy
3 min readJun 3, 2022

Since React Router moved from version 5 to version 6 I’ve been meaning to update any articles and videos I have on this subject to show you the new ways of making protected routes in React. So, here it goes.

Source code

Video tutorial

I want to start with the old way of doing things to give you some context. Maybe you used a different method but this is how I used to do it.

The old way (React Router V5)

I would start by creating a component called <PrivateRoute> and then use this component to check a users permissions and authentication status.

This component would essentially extend the <Route> component to give it some extra functionality and then return back a <Route> with all our parameters and some extra rules on how to handle things.

For simplicity I stripped down the user to a token value of true or false.

import { Route, Redirect } from 'react-router-dom'
const PrivateRoute = ({children, ...rest}) => {
let auth = {'token':false}
return(
<Route {...rest}>
{!auth.token
?
<Redirect to="/login" />
:
children}
</Route>
)
}

We would then take this component and use it just like we would a <Route> inside of our root component (<App>).

function App() {
return (
<div className="App">
<Router>
<PrivateRoute component={HomePage} path="/" exact/>
<Route component={LoginPage} path="/login"/>
</Router>
</div>

So depending on the status of “auth.token” we would render the component passed in or redirect a user.

Why we cant do this anymore

With react router 6 there were some key changes that will affect this structure.

For one, <Redirect> is now <Navigate>.

Next, <Routes> was added so we no longer use <Switch>. With this change <Route> must always be a child to <Routes> which throws out the option of being able to extend our <Route>. The job of the <Routes> component is to look through all the <Route> components and find a match to render to the UI. If we used <PrivateRoute> here it would be ignored by <Routes>. So that’s that.

So what do we do now?

With React Router V6 we now have a component called <Outlet>. The <Outlet> component can be used in a parent <Route> element to render out child elements. So the solution would be to nest private routes inside of a parent route.

The NEW way (React Router V6)

This new approach is actually very clean and makes our component much simpler. With this we will call now <PrivateRoute> “PrivateRoutes”, with an “s”, because we can pass in more than one route inside of this component.

import { Navigate, Outlet } from 'react-router-dom'const PrivateRoutes = () => {
let auth = {'token':true}
return (
auth.token ? <Outlet/> : <Navigate to='/login'/>
)
}

Now to use it we would simply pass in <PrivateRoute> to a <Route>, and then nest all the components that we want to be private inside of this route.

import { BrowserRouter as Router, Routes, Route} from 'react-router-dom'
...
function App() {
return (
<Router>
<Routes>
<Route element={<PrivateRoutes/>}>
<Route path='/' element={<Users/>} />
<Route path='/products' element={<Products/>} />
</Route>
<Route path='/login' element={<Login/>}/>
</Routes>
</Router>
);
}

That’s it!

--

--

Dennis Ivy

YouTuber, contributor at @traversymedia , software developer at @appwrite and online instructor.