Transitioning from React Router v5 to v6
Tags: webdev, react, javascript
Published: 2022-01-11 01:06:02


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](https://remix.run/blog/react-router-v6)  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:


```jsx
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


```jsx
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:


```jsx
<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,

```jsx
<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:

```jsx
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:

```jsx

<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:

```jsx
<Route path="/faq">
   <Redirect to="/about" />
</Route>;
<Route path="/about" component={About} />;
``` 



But in v6, we will do it as so:

```jsx
<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:

```jsx
<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:

```jsx
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:

```jsx
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:


```jsx
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. 
```jsx
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

```jsx
// 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 the `Route` component.
- `activeClassName` and `activeStyle` props have been removed from `NavLink`.
- We can access the isActive state of a NavLink component through a function callback.
- `Redirect` component has been replaced with `Navigate` 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](https://bundlephobia.com/) 

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** ✌️