react-router v6 demystified (part 1)
Tags: react, javascript, tutorial, reactnative
Published: 2021-08-17 13:35:49

`react-router` version 6 is currently in beta. I guess it will be released soon. So, what a great time to explore this library which is one of the most used routing library for React.

In this series of article, we are going to briefly see the changes compared to the previous version, how to use it and then we will write the specifications and code our own implementation the same way `react-router` is implemented, it will just an idea of how it's really implemented, it won't have all the feature, but after reading all the article you will be able to explore it on your own on the repository.

Let's get it started.

## Difference between v5 and v6

### New `Route` API

The `Route` component has a new API which is a lot simpler. 
The props available are:
- `path` (default value "/")
- `element` which is the element to display. No more `render` or `component`
- `caseSensitive` instead of `sensitive` to tell that the path needs to match with the same case. Default value to false.

In version 5:

```jsx
<Route path="/" component={HomePage} />

// or

<Route path="/">
  <HomePage />
</Route>
```

In version 6:

```jsx
<Route path="/" element={<HomePage />} />
```

You can nest `Route` together. For example:

```jsx
<Route path="hobby" element={<HobbyLayout />}>
  <Route
    path="favorite"
    element={<FavoriteHobbyListBody />}
  />
  <Route path=":name" element={<HobbyDetailBody />} />
</Route>

// with

function HobbyLayout() {
  return (
    <>
      <h1>Hobby layout page</h1>
      {
        // Will display the right nested Route
      }
      <Outlet />
    </>
  );
}
```

> **Note:** As you can see the above example, it's still possible to define path with path params (ex `:name`).


### Welcome to `Routes` component

The version 6 introduces a new component name `Routes` which is kindly the equivalent to the `Switch` component which is no more present.

And it's also possible to nest `Routes`

In version 5:

```jsx
function App() {
  return (
    <BrowserRouter>
      <Switch>
        <Route path="hobby" component={HobbyPage} />
      </Switch>
    </BrowserRouter>
  );
}

function HobbyPage() {
  const match = useRouteMatch();

  return (
    <Switch>
      <Route path={`${match.path}/favorite`}>
        <FavoriteHobbyListBody />
      </Route>
      <Route path={`${match.path}/:name`}>
        <HobbyDetailBody />
      </Route>
    </Switch>
  );
}
```

In version 6 it becomes:

```jsx
function App() {
  return (
    <BrowserRouter>
      <Routes>
        <Route path="hobby/*" element={<HobbyPage />} />
      </Routes>
    </BrowserRouter>
  );
}

function HobbyPage() {
  return (
    <Routes>
      <Route
        path="favorite"
        element={<FavoriteHobbyListBody />}
      />
      <Route path=":name" element={<HobbyDetailBody />} />
    </Routes>
  );
}
```

> **Note:** You may have noticed the trailing `/*` to parent `Route` which is important to tell "match deeply".

Perfect example to go to the next part about relative path.

### Relative Routes and Links

As you can see in the example above, for the routes path you don't have to take care about the `match.url` anymore. All path are relatives now, unless you specify it's an absolute path by starting your path with a slash, for example:

```jsx
// If I am in the `Route` element with the path
// `/hobby`


// Will navigate to `/hobby/favorite`
<Link to="favorite">Favorite hobby link</Link>

// Will navigate to `/about`
<Link to="/about">About page link</Link>


// Route for path `/hobby/favorite`
<Route
  path="favorite"
  element={<FavoriteHobbyListBody />}
/>

// Watch out it is also route for `/hobby/favorite`
<Route
  path="/favorite"
  element={<FavoriteHobbyListBody />}
/>
```

### Routes element are sorted by `react-router`

This is a cool feature, you don't have to care anymore of the sorting of your `Route` elements inside the `Routes`. 

`react-router` will do a smart sorting, you don't have to be afraid that the first `Route` defines "blocks" all next ones.

For example in version 5:

```jsx
function App() {
  return (
    <BrowserRouter>
      <Switch>
        <Route path="/">
          <HomePage />
        </Route>
        <Route path="/hobby">
          <HobbyPage />
        </Route>
      </Switch>
    </BrowserRouter>
  );
}

function HomePage() {
  return <p>Home page</p>;
}

function HobbyPage() {
  return <p>Hobby page</p>;
}
```

Even on the url `/hobby`, you will see the content of `HomePage`.

Not the case anymore in version 6 with the code below:

```jsx
function App() {
  return (
    <BrowserRouter>
      <Routes>
        <Route path="/" element={<HomePage />} />
        <Route path="/hobby" element={<HobbyPage />} />
      </Routes>
    </BrowserRouter>
  );
}

function HomePage() {
  return <p>Home page</p>;
}

function HobbyPage() {
  return <p>Hobby page</p>;
}
```

### More changes

I only have listed the more important changes, but there are more. If you want read a well written, with all changes listed, you can read the [migration guide to v6](https://github.com/remix-run/react-router/blob/dev/docs/advanced-guides/migrating-5-to-6.md#upgrade-all-switch-elements-to-routes).


----

## Specifications for our implementation

Before doing some code, let's describe the specifications:
- a `Routes` can only return ONE matching `Route`
- we want to be able to nest `Route` together. For example:

```jsx
<Route path="hobby" element={<HobbyPageLayout />}>
  <Route path="/" element={<HobbyListBody />} />
  <Route path="favorite" element={<FavoriteHobbyBody />} />
</Route>
```

In this case the matching nested `Route` will be accessible by a component named `Outlet`. For example, for the `HobbyPageLayout` above :

```jsx
function HobbyPageLayout() {
  return (
    <>
      <p>Hobby page layout</p>
      {
        // Will be basically HobbyListBody,
        // FavoriteHobbyBody or undefined
      }
      <Outlet />
    </>
  );
}
```


- we want to be able to define path variable and be able to access it through `useParams` hook. For example:

```jsx
<Route path="hobby/:name" element={<HobbyDetailPage />} />

const { name } = useParams();
```

> **Note:** Of course, we want to be able to combine nested `Route` with path variable.

- we want to be able to do nested `Routes`. But we do not want the API to be hard. In the nested we do not want to repeat the parent pathname (or using `match.path`). For example:

```jsx
<Routes>
  <Route path="hobby/*" element={<HobbyPage />} />
</Routes>

function HobbyPage() {
  return (
    <Routes>
      <Route path="/" element={<HobbyListBody />} />
      <Route
        path="favorite"
        element={<FavoriteHobbyBody />}
      />
      <Route path=":name" element={<HobbyDetailPage />} />
    </Routes>
  );
}
```

- we do not want to care about slash while doing nested routes

- we want to have relative path during navigation and be  able to do absolute with leading "/" to our path.

- have hooks to defines routes :D

> **Note:** You have probably noticed the `*` to tell match all.


Now in the next article we are going to start the implementation, starting small:
- no nesting `Route` and `Routes`
- no path params
- no relative path

----

## Playground

Here is a little code sandbox of `react-router` v6:

{% codesandbox react-router-article-part-1-07zkb %}

----

## Conclusion

We have seen the new `react-router` v6 API, that I find simpler and clearer. Relative link are really cool, nor more question to ask ourself like "Do I need a leading/trailing slash for my path?", and will remove a lot of boilerplate code with `match.url` and `match.path`.

Specifications are ready for our in coming implementations and explorations.

For the [next article](https://dev.to/romaintrotard/react-router-v6-demystified-part-2-19a9), you will need to know the location and history API, you can read [my article](https://dev.to/romaintrotard/let-s-explore-javascript-s-location-and-history-api-26pc). And be sure, to be comfortable with React context, spoiler alert: there are quite a lot.


---- 

Want to see more ? Follow me on [Twitter](https://twitter.com/romain_trotard ) or go to my [Website](https://www.romaintrotard.com). :panda_face: