Changes In React-router V6

Dev. | Creator. | Writer.
With the release of react-router 6 came so many changes, deprecation, and lack of backward compatibility to its previous versions, which has left react developers with no choice but embrace to its new changes since literally every react project out there needs routing and therefore uses the react-router library.
INTRODUCTION
This is not a beginner react-router article, It’s for people that want to upgrade a project from react-router version 5 to version 6, and also someone that wants to learn about the new changes brought by react-router version 6.
To follow this article, you need a basic knowledge of react and the react-router library.
By the end of this article, you will learn what’s new in react-router version 6, and how to apply the changes to an existing app that’s using react-router version 5.
TABLE OF CONTENTS
- ROUTE DECLARATION
- NESTED ROUTES
- NAVLINK
- NAVIGATE
- OUTLET
- IMPERATIVE NAVIGATION
- CONCLUSION
ROUTE DECLARATION
This is one of the first changes to react-router v6, The Switch component was used in react-router v5 to wrap our routes to make sure only a single route is loaded at a time instead of concurrently loading all matched route.
In react-router v6 Switch doesn’t exist anymore, Switch has been replaced with Routes .
import { Route, Routes } from 'react-router-dom';
<Routes> // Switch
<Route path='/home'>
<Home />
</Route>
<Route path='/shop' exact>
<Shop />
</Route>
</Routes>
We still have the Route component and it still takes the path prop, but the component that should be rendered when a given path becomes active is no longer a child of the Route, but instead on Route you add the new element prop and then pass a dynamic value to the it, which is the component that is to be rendered passed in as JSX.
<Routes>
<Route path='/home' element={<Home />}/>
<Route path='/shop' element={<Shop />}/>
</Routes>
With react-router 6 the internal logic for evaluating path and then picking a route to load changed, this means we no longer have to use exact, it automatically picks the match for the exact path if we define our route how we have done above. But if you want the old behavior of matching the start of a path only you can do this.
<Route path='/shop/*' element={<Shop/>} />
By adding /* after your path, the path becomes active if a URL path starts with /shop is matched.
NESTED ROUTES
Nested route can be useful because it gives us the ability to build deeply nested page structures. In the code below you will see the power of react-router nested routes, which is one of my best features of react-router v6, it’s has this really clean and intuitive syntax
import { Route, Routes, Link } from 'react-router-dom';
function App() {
return (
<div>
<Routes>
<Route path='/home' element={<Home />} />
🚩 <Route path='/shop' element={<Shop />}>
<Route path='shoes' element={<Shoes />}/>
<Route path='hats' element={<Hats />}/>
🚩 </Route>
</Routes>
</div>
);
}
function Shop() {
return (
<>
<div>
<h1>Shoes</h1>
<Link to='/shop/shoes'>Shoes</Link>
</div>
<div>
<h1>Hats</h1>
<Link to='/shop/hats'>Hats</Link>
</div>
</>
);
}
export default App
Here first of all note, the syntax that is used to nest the routes in App(), the route🚩
<Route></Route> is not a self-closing component it has a closing tag, While the routes inside of it are self-closing routes. Notice how the paths are declared, there was no slash used path='/shoes ' (this will give an error), react-router internally knows that we are already on the shop route, it just matches the routes nested to it automatically path='shoes'.
NAVLINK
In react-router v6 we still have the Link component which still maintains its normal behavior, and we also have the NavLink component which also did not change, however, what did change is the activeClassName and activeStyle props passed to the NavLink.
Version 5
<NavLink activeClassName={classes.active} to='/home'>
Home
</NavLink>
In react-router version 5 you could use the activeClassName prop to apply a css class automatically to the link anytime it becomes active, But in react-router v6 this prop was removed.
Version 6
<nav>
<li>
<NavLink className={(navData) => navData.isActive ? classes.active : ''}
to='/home'>
Home
</NavLink>
</li>
</nav>
<nav>
<li>
<NavLink
style={({ isActive }) => {
return {
textColor: isActive ? 'red' : 'green',
};
}}
to='/home'
>
Home
</NavLink>
</li>
</nav>
In react-router v6 if you want to apply a specific class once the link is active, You have to manually check if the link is active or not. To do this you use either the className or style prop, both props work in a unique way when applied to NavLink. The className prop doesn’t just take a class name, It also takes a function that will give us some information about the link and current state of navigation.
The navData argument in the function above is provided by react-router, and the function will be executed by react-router when it evaluates NavLink and whenever navigation changes. Inside the navData object provided by react-router, is the isActive property which will be true if the link is active for the currently chosen path or not. navData.isActive is used to return a class name dynamically that is applied to NavLink. The style prop syntax is technically the same with the className, but you use what suits you best.
NAVIGATE
React-router is full of hooks but Navigate is not one of them, Navigate is a component in react-router v6 that is used to navigate between pages, It’s similar to the Link component but they are not the same, It’s default behavior is to put whatever page we are navigating to at the very top of the router, which can cause some serious problems if that’s not we want, but we can fix this behavior by using the replace keyword.
import { Route, Routes, Navigate } from 'react-router-dom';
<Routes>
<Route path='/home' element={<Navigate replace to='/shop' />}/>
<Route path='/login' element={<Login />}/>
</Routes>
By using the replace keyword this will be an actual route of /shop .
import { Route, Routes, Navigate } from 'react-router-dom';
<Routes>
<Route path='/' element={<Navigate to='/home' />}/>
<Route path='/login' element={<Login />}/>
</Routes>
Here we are actually navigating to the home page, which is actually the first page, we want to route to in our app, so the replace keyword is not needed.
OUTLET
This react-router component is simply a place-holder that tells react-router where nested route content should be inserted.
import { Route, Routes, Link, Outlet} from 'react-router-dom';
function App() {
return (
<div>
<Routes>
<Route path='/home' element={<Home />} />
<Route path='/shop' element={<Shop />}>
<Route path='shoes' element={<Shoes />}/>
<Route path='hats' element={<Hats />}/>
</Route>
</Routes>
</div>
);
}
function Shop() {
return (
<>
<div>
<h1>Shoes</h1>
<Link to='/shop/shoes'>Shoes</Link>
</div>
<div>
<h1>Hats</h1>
<Link to='/shop/hats'>Hats</Link>
</div>
<Outlet/>
</>
);
}
const Shoes = () => <> <h1> This is the Shoes page </h1> </>
const Hats = () => <> <h1> This is the Hats page </h1> </>
export default App
Here the nested shop routes above in the App component are placed in the Shop component, so when the shop component the element rendered by the nested Shop route, is displayed in the shop component, which is the Shoes and Hats components respectively.
IMPERATIVE NAVIGATION
Imperative navigation occurs when a user is redirected as a result of an event that occurs on a route, such as when a button is clicked, completion of an API request, and whatnot.
USENAVIGATE
The useNavigate hook is a form of imperative navigation, In v5, we use the useHistory hook to do something like:
Version 5
import { Route, Routes, Link, useHistory} from 'react-router-dom';
function Shop() {
const history = useHistory();
history.replace()
history.push()
return (
<>
<div>
<h1>Shoes</h1>
<Link to='/shop/shoes'>Shoes</Link>
</div>
<div>
<h1>Hats</h1>
<Link to='/shop/hats'>Hats</Link>
</div>
</>
);
}
Here the useHistory hook is been used which gives us a history object, where we could use the push() and replace() methods to push or replace a route to navigate.
Version 6
import { Route, Routes, Link, useNavigate} from 'react-router-dom';
function Shop() {
const navigate = useNavigate();
return (
<>
<div>
<h1>Shoes</h1>
<Link to='/shop/shoes'>Shoes</Link>
<button onClick={() => {navigate('/home', {replace:true})}}>
Homepage
</button>
</div>
<div>
<h1>Hats</h1>
<Link to='/shop/hats'>Hats</Link>
<button onClick={() => { navigate(-1) // -2 or 1}}>
Homepage
</button>
</div>
</>
);
}
Now using the useNavigate hook in react-router v6, when we execute useNavigate(), we get a navigate object and function(navigate()). The navigate() can be executed to navigate somewhere else, from the example above we are navigating to the home page, we can also move forward and backward between pages, by using numbers. navigate(-1) goes to the previous page, navigate(1) goes to the next page. You can still use redirect by passing this argument to navigate navigate( '``/home``'``, { replace : true }). This will replace the current route with the new one, which will redirect instead of pushing the new route onto the navigation stack.
CONCLUSION
Highlights
Routescomponent has replacedSwitchcomponent .- Rendered components are now placed as JSX in the
elementprop of theRoutecomponent - The
exactprop have been removed from theRoutecomponent. - The
activeStyleandactiveClassNameprops have been removed fromNavLink. - The
isActivestate of aNavLinkcomponent can be accessed through a callback function. - The
useHistoryhook has been replaced withuseNavigatehook. - A better way to implement nested routes.
- The
outletcomponent, a placeholder for nested routes




