Aman Mittal 👨‍💻Developer 👉 Nodejs, Reactjs, ReactNative | Tech Blogger with 1M+ views on Medium | My weekly dev newsletter 👉 tinyletter.com/amanhimself

A guide to using React Router v6 in React apps

7 min read 2009

Single page applications (SPAs) with multiple views need to have a mechanism of routing to navigate between those different views without refreshing the whole web page. This can be handled by using a routing library such as React Router.

In this tutorial, let’s take a look at how to create routes using the React Router v6 library. Do note that, at the time of writing this post, React Router v6 is still in beta. This tutorial is going to give you a peek into some of the new features the library is coming out with.

If you have experience working with routing in React apps, you may already know that over the past few years Reach Router has gained some attention. However, it is getting merged back into the React Router library starting from version 6. This does mean that the v6 version has a smaller bundle size than its previous versions, one of the major reasons that Reach Router exists.

Prerequisites

To take full advantage of this tutorial, please make sure you have the following installed in your local development environment:

  • Node.js version >= 12.x.x installed
  • Access to one package manager such as npm or yarn or npx
  • Basic knowledge of JavaScript, Reactjs, and React Hooks

Getting started

Start by creating a new React app. Use the following command from a terminal window to generate the project directory, then navigate inside the project directory and install required dependencies to add React Router v6 library:

npx create-react-app react-router-v6-example
cd react-router-v6-example
yarn add history react-router-dom@next

Once the dependency is installed open the package.json file in your favorite code editor and you are going to see the dependency version of the react-router-dom library:

“dependencies": {
    // rest of the dependencies installed
    "react-router-dom": "6.0.0-beta.0",
  },

Different packages in React Router library

React Router library contains three different npm packages and each of the following packages has a different purpose:

  • react-router
  • react-router-dom
  • react-router-native

The package react-router is the core library that is used as a peer dependency for the other two packages listed above. The react-router-dom is the package that is used in React apps for routing. The last package in the list, react-router-native has bindings to be used in developing React Native applications.

Now that we have that covered, let’s build the first route.

Creating the first route with React Router v6

To create the first route using React Router library, open src/App.js file and add the following import statement:

We made a custom demo for .
No really. Click here to check it out.

// after other import statements
import { BrowserRouter as Router } from 'react-router-dom';

This is the first component to import from the react-router-dom library. It is used to wrap different routes. It uses the HTML5 history API to keep track of routes history in the React app.

The Router part in the above snippet is the alias that makes it easier to write. It is recommended to import and use it at the top level component in a React app’s component hierarchy:

function App() {
  return <Router>{/* All routes are nested inside it */}</Router>;
}

The next component to import from react-router-dom is the new Routes:

import { BrowserRouter as Router, Routes } from 'react-router-dom';

This new element is an upgrade of the previous Switch component. It includes features like relative routing and linking, automatic route ranking, nested routes, and layouts.

The last component from react-router-dom required is called Route and is responsible for rendering the UI of a React component. It has a prop called path which always matches the current URL of the application. The second required prop is called element that tells the Route component when a current URL is encountered and which React component to be rendered. The element keyword here is also a new addition. Previously, with React Router v5, you would be using the prop called component.

To create the first route in the following demo, let’s create a basic function component called Home that returns some JSX:

function Home() {
  return (
    <div style={{ padding: 20 }}>
      <h2>Home View</h2>
      <p>Lorem ipsum dolor sit amet, consectetur adip.</p>
    </div>
  );
}

Next, update the App function component with the following route. Another feature of v6 library to notice here is that the element prop of a Route component now allows you to pass a React component rather than just the name of that React component. This makes it easy to pass props down the routes:

function App() {
  return (
    <Router>
      <Routes>
        <Route path="/" element={<Home />} />
      </Routes>
    </Router>
  );
}

To see it working, go back to the terminal window and start the development server using the command yarn start. Next, visit the URL http://localhost:3000 in a browser window.

Here is the output after this step:

local host page with the words `Home View` as a title and lorem ipsum in the body

Let’s quickly create another function component called About that is only rendered when the URL in a browser window is http://localhost:3000/about:

function About() {
  return (
    <div style={{ padding: 20 }}>
      <h2>About View</h2>
      <p>Lorem ipsum dolor sit amet, consectetur adip.</p>
    </div>
  );
}

Then, add the Route for the About component:

<Routes>
  <Route path="/" element={<Home />} />
  <Route path="/about" element={<About />} />
</Routes>

Now, go back to the browser window and visit the URL http://localhost:3000/about:

local host page words "About View" and lorem ipsum

Adding a navigation menu

To navigate at a particular route within the React app, or the two currently existing routes in the demo app, let’s add a minimal navigation bar with the help of the Link component from react-router-dom.

Begin by importing it from the library:

import { BrowserRouter as Router, Routes, Route, Link } from 'react-router-dom';

The concept of navigating between different web pages in HTML is to use an anchor tag:

<a href="">Some Link Name</a>

Using this approach in a React app is going to lead to refreshing a web page, each time a new view or page itself is rendered. This is not the advantage you are looking for when using a library like React. To avoid the refreshing of the web pages, the react-router-dom library provides the Link component.

Next, inside the App function component, create a nav bar as shown in the code snippet:

<Router>
  <nav style={{ margin: 10 }}>
    <Link to="/" style={{ padding: 5 }}>
      Home
    </Link>
    <Link to="/about" style={{ padding: 5 }}>
      About
    </Link>
  </nav>
  {/* Rest of the code remains same */}
</Router>

Go to the browser window to see the navigation bar in action:
arrow hovering over home and view pages

How to handle nested routes

Nesting routes is an important concept to understand. When routes are nested, it is generally assumed that a certain part of a web page remains constant and only the child part of the web page changes.

For example, if you visit a simple blog, the title of the blog is always displayed, then a list of blog posts is displayed beneath it. However, when you click a blog post, the list of blog posts is replaced by the contents or the description of that specific blog post. This is an example that is going to be performed in this section to understand how to handle nested routes in the latest version of the React Router library.

In React Router v5, nested routes have to be defined explicitly. This is not the case with React Router v6. It picks one of the best elements from the React Router library called Outlet to render any matching children for a particular route. To start, import the Outlet from the react-router-dom library:

import {
  // rest of the elements/components imported remain same
  Outlet
} from 'react-router-dom';

To mimic a basic blog, let’s add some mock data in the App.js file. The code snippet consists of an object called BlogPosts that further consists of different objects as properties. Each object is constituted of three things:

  • a unique slug of a post
  • title of that post
  • description of that post
const BlogPosts = {
  '1': {
    title: 'First Blog Post',
    description: 'Lorem ipsum dolor sit amet, consectetur adip.'
  },
  '2': {
    title: 'Second Blog Post',
    description: 'Hello React Router v6'
  }
};

This unique slug is going to be used in the URL of a web browser to see the contents of each post. Next, create a function component called Posts where a list of all blog posts are displayed:

function Posts() {
  return (
    <div style={{ padding: 20 }}>
      <h2>Blog</h2>
      {/* render any matching child */}
      <Outlet />
    </div>
  );
}

Define another component called PostLists that is going to display a list of all posts whenever the URL in the browser window hits http://localhost:3000/posts. Let’s use JavaScript Object.entries() method to return an array from the object BlogPosts. This array is then mapped to display a list of titles of all blog posts:

function PostLists() {
  return (
    <ul>
      {Object.entries(BlogPosts).map(([slug, { title }]) => (
        <li key={slug}>
          <h3>{title}</h3>
        </li>
      ))}
    </ul>
  );
}

Modify the routes in the App function component like this:

<Routes>
  {/* Rest of the code remains same */}
  <Route path="posts" element={<Posts />}>
    <Route path="/" element={<PostLists />} />
  </Route>
</Routes>

This indicates that whenever the URL http://localhost:3000/posts is triggered, a list of blog posts is going to be rendered, hence, the component PostsLists:

posts page with arrow pointing to the url saying "the current url" and words in the window saying "Blog: first blog, second blog"

How to access URL parameters and dynamic parameters of a route

To visit the individual post by clicking the post title from the rendered list of blog posts, all you have to do is, wrap the title of each post within a Link component in the PostsLists component. Then, define the path to each post using the slug of each post. The /posts/ prefix allows the path in the web browser to be consistent:

<ul>
  {Object.entries(BlogPosts).map(([slug, { title }]) => (
    <li key={slug}>
      <Link to={`/posts/${slug}`}>
        <h3>{title}</h3>
      </Link>
    </li>
  ))}
</ul>

Next, import a hook called useParams from the react-router-dom library. This hook allows you to access any dynamic parameters that a particular route (or slug, in this case) may have. The dynamic parameters for each slug are going to be the title and the description of each blog post. The need to access them is to display the content of each blog post when a particular slug of a blog post is triggered as the URL in the browser window:

import {
  // rest of the elements/components imported remain same
  useParams
} from 'react-router-dom';

Create a new function component called Post. This component is going to get the current slug of the post from useParams hook. Using the bracket square notation syntax in JavaScript, a new post variable is created that has the value of the properties or the current contents of a blog post. Destructuring the contents of this post variable, you can render them:

function Post() {
  const { slug } = useParams();
  const post = BlogPosts[slug];
  const { title, description } = post;
  return (
    <div style={{ padding: 20 }}>
      <h3>{title}</h3>
      <p>{description}</p>
    </div>
  );
}

Lastly, add a dynamic route called :slug in the App function component to render the contents of each post:

// Rest of the code remains same
<Route path="posts" element={<Posts />}>
  <Route path="/" element={<PostLists />} />
  <Route path=":slug" element={<Post />} />
</Route>

Here is the complete output after this step:
arrow navigating from home view to about view to posts page

Conclusion

Hopefully, this post provides you a great introduction if you are learning React Router for the first time. If you are already familiar with any of the previous versions of this routing library, I hope this post gives you an overview of the changes between the previous and the latest version.

Source code available at this GitHub repository.

Full visibility into production React apps

Debugging React applications can be difficult, especially when users experience issues that are difficult to reproduce. If you’re interested in monitoring and tracking Redux state, automatically surfacing JavaScript errors, and tracking slow network requests and component load time, try LogRocket.

LogRocket is like a DVR for web apps, recording literally everything that happens on your React app. Instead of guessing why problems happen, you can aggregate and report on what state your application was in when an issue occurred. LogRocket also monitors your app's performance, reporting with metrics like client CPU load, client memory usage, and more.

The LogRocket Redux middleware package adds an extra layer of visibility into your user sessions. LogRocket logs all actions and state from your Redux stores.

Modernize how you debug your React apps — .

Aman Mittal 👨‍💻Developer 👉 Nodejs, Reactjs, ReactNative | Tech Blogger with 1M+ views on Medium | My weekly dev newsletter 👉 tinyletter.com/amanhimself

Leave a Reply