React-router is the standard library for routing in react js. It allows users of a react app to move between different sections (components) of the app.
The react-router team announced the release of a stable version of react-router version 6 (v6) towards the end of 2021, but switching from react-router version 5 (v5) to v6 may be difficult due to some big breaking API changes. In this article, we will walk through what is new in v6 and how to upgrade an existing React project from v5 to v6.
To upgrade the version of the react-router package in our app, we navigate to the project folder and run
npm install react-router-dom@[VERSION_NUMBER]
Replace VERSION_NUMBER
with the version we want to install, or with “latest” if we want the latest version, like so:
npm install react-router-dom@6
OR
npm install react-router-dom@latest
Note that we have to be connected to the internet to be able to complete the installation else the installation will fail. Also, ensure that the version of react in your project is v16.8 or greater because react-router v6 relies heavily on hooks that were first supported by react v16.8
Switch
is replaced with Routes
The first casualty of the v5 era is the Switch
component. The Switch
component is used to wrap our routes and it ensures that just one matching route is loaded per time. But this does not exist in v6 anymore. We now use the Routes
component to do the same thing that Switch
does. Note that we still import BrowserRouter
to wrap our app in, just as is done in v5.
In v5, we did it as so:
import { BrowserRouter, Switch } from "react-router-dom";
function App() {
return (
<BrowserRouter>
<div className="App">
<Switch>
{" "}
{/* Individual Routes come in here */}
</Switch>
</div>
</BrowserRouter>
);
}
export default App;
But in v6, this is how we will do it
import { BrowserRouter, Routes } from "react-router-dom";
function App() {
return (
<BrowserRouter>
<div className="App">
<Routes>
{" "}
{/* Switch changes to Routes */}
{/* Individual Routes come in here */}
</Routes>
</div>
</BrowserRouter>
);
}
export default App;
Update in the definition of Route
component
Although the Route
component still maintains a spot in v6, the way we will define it is different from the way we did in v5. We will no longer place the component we want to render in any of the ways we did it in v5, we will instead pass it as the value of the element
prop.
No more exact
prop
In v5, without adding exact
as a prop to a Route
component, the path will match if a URL starts with the path keyword, and because the matching process is in order from top to bottom. But in v6, we would not be needing the exact
prop because the path pattern matching algorithm was changed, and is even more enhanced now.
In v5, we did it as so:
<Switch>
{/* There were 3 ways we declared routes in v5 */}
<Route path="/signup" component={Product} />
{/* OR */}
{/* This method allows us pass props to the rendered component */}
<Route path="/games">
<Product id={2} />
</Route>
{/* OR by passing a callback to the render prop */}
<Route path="/games" render={(props) => <Product {...props} />} />
</Switch>;
In v6,
<Routes>
{" "}
<Route path="/games" element={<Product />} />
{/* And with props for the rendered component */}
<Route path="/movies" element={<Product id={200} category="shirt" />} />
</Routes>;
Links
and NavLinks
Link
and NavLink
components still take their places in v6. The Link
component generally works as it did in v5, but with the NavLink
component, the activeClassName
and the activeStyle
prop were removed. In v5, activeClassName
prop was used to apply some CSS classes automatically to the link once it became active, and the activeStyle
allowed us to add internal styles to a link when it became active.
But in v6, we can now use a function which information about the active state of the link. The function’s parameter is an object with the property isActive
. This property is true when the link is active and false when it is not. The value of isActive
now allows us to use conditional expressions to indicate an active style or class name(s).
In v5, we did it as so:
import {NavLink} from “react-router-dom”
{/* … */}
<NavLink
to="/product"
style={{ color: "#689" }}
activeStyle={{ color: "#3072c9" }}
className="nav_link"
activeClassName="active"
>
Products
</NavLink>;
But in v6, we will do it as so:
<NavLink
to="/product"
style={({ isActive }) => ({ color: isActive ? "#3072c9" : "#689" })}
className={({ isActive }) => `link${isActive ? " active" : ""}`}
>
Product
</NavLink>;
Redirect
has made way for Navigate
In v5, we used the Redirect
component to take one to another page but it is no longer exported from react-router-dom in v6. It has been replaced with the Navigate
component.
In v5, we did it as so:
<Route path="/faq">
<Redirect to="/about" />
</Route>;
<Route path="/about" component={About} />;
But in v6, we will do it as so:
<Route path="/games" element={<Navigate to="/about" />} />;
<Route path="/games" element={<About />} />;
It is important to note that if we just added the Navigate
component the way we did in the snippet above, it will only push our navigation to this path onto the navigation stack, but if we intend to replace the current page with a new page, we will add the replace prop to the Navigate
component as in so:
<Route path="/games" element={<Navigate replace to="/about" />} />;
Nested Routes
Nested routes, as the name implies, are routes placed in another route. They are used to render more specific information in child components. In v6, we place our nested routes as children of our parent route. Then we introduce the Outlet
component, which is exported from react-router-dom in the rendered component to specify where we want the nested information to be displayed. The Outlet component is not necessary but it makes the code cleaner.
In v5, we did it as so:
import { useRouteMatch } from "react-router-dom";
function App() {
return (
<BrowserRouter>
<Switch>
<Route exact path="/about" component={About} />
<Route path="/product" component={Product} />
</Switch>
</BrowserRouter>
);
}
function Product() {
let match = useRouteMatch();
return (
<div>
<Switch>
{/* match.path returns the path specified in parent route. In this case it is "/product" */}
<Route path={`${match.path}`}>
<AllProducts />
</Route>
{/* And in this case it is /product/:id */}
<Route path={`${match.path}/:id`}>
<ProductDetail />
</Route>
</Switch>
</div>
);
}
In v6, we do it as so:
import { Outlet } from "react-router-dom";
function App() {
return (
<Routes>
<Route path="/about" element={<About />} />
<Route path="/product" element={<Product />}>
{/* Here the paths of the nested routes are relative to the path of the parent route. */}
{/* This becomes "/product/" */}
<Route path="/" element={<AllProducts />} />
{/* And This becomes "/product/:id" */}
<Route path="/:id" element={<ProductDetail />} />
</Route>
</Routes>
);
}
function Product() {
return (
<Container>
<>
<div>Product</div>
{/* Other content of the parent component */}
</>
{/* This is where the nested information begins */}
<Outlet />
</Container>
);
}
Programmatic Navigation
Programmatic navigation occurs when a user is redirected as a result of an event that occurs on a route, such as clicking a button, an API request completing, e.tc. In v5, we could use the useHistory
hook to do something like:
import { useHistory } from "react-router-dom";
function Product() {
const history = useHistory();
const handleClick = () => {
//This pushes the new route on top of the navigation stack
history.push("/new-route");
//This replaces the current route with the new route in the navigation stack
history.replace("/new-route");
};
return (
<div>
<button>Click Me to redirect to new route</button>
</div>
);
}
But in v6, useHistory
hook is replaced with useNavigate
hook, and we use it in different ways.
import { useNavigate } from "react-router-dom";
function Product() {
const navigate = useNavigate();
const handleClick = () => {
//This pushes the new route on top of the navigation stack
navigate("/new-route");
//This replaces the current route with the new route in the navigation stack
navigate("/new-route", { replace: true });
};
return (
<div>
<button>Click Me to redirect to new route</button>
</div>
);
}
One cool thing is that we can go forward and backward any number of times on the navigation stack. By using a positive number as a parameter to navigate()
above, the route moves that number of steps forward. And a negative number does the same thing backward
// Goes forward
navigate(1)
// Goes forward twice
navigate(2)
// Goes backward
navigate(-1)
// Goes backward three times
navigate(-3)
The Prompt
Component
The Prompt
component in v5 prevents accidentally leaving a page if there are unsaved changes. But react-router team did not include it in v6, and there is no alternative for it. So if you need the feature, you either manually implement it or move back to v5.
In addition to not including Prompt
in the current release, useBlocker
and usePrompt
also do not work. The react-router team although said in the official docs that they are currently working on adding it back to v6 at some point, but not for the first stable release of 6.x.
Summary
Let us highlight the changes we have gone through.
Switch component is replaced with Routes component.
Changes in how to place the rendered component of Route.
No more
exact
prop in theRoute
component.activeClassName
andactiveStyle
props have been removed fromNavLink
.We can access the isActive state of a NavLink component through a function callback.
Redirect
component has been replaced withNavigate
component.A sleeker way to implement nested routes.
In conclusion, if you think you are not ready to make the switch to v6 from v5 or any other version, you can always install a previous version using this.
npm install react-router-dom@[VERSION_NUMBER]
But then, you will be missing out on some goodies that came with v6 including but not limited to:
- Enhanced path pattern matching algorithm.
- Bundle size reduced by 60% according to Bundlephobia
I believe we were able to successfully make that switch to react-router v6 and stop using the Switch component (pun very much intended) 😌.
Have a great coding experience 🙌.
Signing out,
Steph Crown ✌️
Top comments (2)
Excelent Info! I was looking for a quick explanation of new router dom
I am glad you found it useful.