How to get started with routing in React apps with React Router

React itself is focused on building user interfaces, and it lacks a fully integrated routing solution. React Router is React's most popular routing library. Check how to get started with React Router

React is a popular library for developing single-page applications (SPAs) that can be rendered on the client-side. In a SPA, the end-user who knows Multi Pages expects the following features: Routing is the procedure that keeps the browser URL in sync with the content.

Using declarative routing, you can control the application’s traffic information using the expression “the route should look like this…” and place components wherever you have a preference. In reality, it is a third-party library known for its simplicity.

What is React Router?

React itself is focused on building user interfaces, and it lacks a fully integrated routing solution. React Router is React’s most popular routing library. It allows you to define paths in the same declarative style as most other libraries. A router allows your application to navigate by changing its browser’s URL or browsing history while staying in synch with other elements.

Understanding routes

The matching logic to a component is delegated to the path-to-regexp library. With this behavior, you set which component should be displayed for a specific URL path.

It means that if you have, for example, an “about me” page and the path of this page is “/about-me,” you need to assign a component that Reacts will render for that specific path.

What does React Router DOM do?

React Router DOM allows you to implement dynamic routing in web apps. React RouterDOM supports component-based routing according to the app’s requirements and the framework. In contrast, the traditional routing architecture provides routing services in a configuration outside of a currently active app. React Router is the best solution for creating React Applications that run in the browser. React router DOM is the quickest way to create routing in React.

Let’s dive in!

This tutorial is split out among multiple areas. Our first task is to a create React app and install React Router using npm. Now we’ll get down to some basic features of the React Router. Each concept and system for constructing these routes will be discussed along the course.

The full code for the project is published at this GitHub repository. This tutorial presents concepts of using React routing, the basics of React, hooks, and testing.

Prerequisites

I tested the code in Node 14.17.3. I set up the project using Create React App. You will also need a basic knowledge of JavaScript, HTML & CSS add React to understand what is going on here, but if you need to learn React Router, you are familiar with those things.

By The Way: HTML means HyperText Markup Language, so it’s not a sexually transmitted disease.

Scaffold the project

As I mentioned before, you’ll require the Node installed on your computer for this tutorial. Then you can follow these instructions to set up React project using Create React App.

Changes after this: step: Scaffold the project

Setting up React Router

Now you can install React Router by using npm or yarn. Let’s use npm

$ npm install react-router-dom@6

Changes after this: step: Setting up React Router

Cleanin’ Out My Closet

Before we go deeper, let’s clean some code that CRA generated for us.

Replace index.js with this content:

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

ReactDOM.render(
    <App />,
    document.getElementById('root')
);

Remove these files: reportWebVitals.js, index.css, logo.svg, App.css, and App.test.js -who needs tests??? But wait, we can add our own tests later, don’t worry, I am just trolling you.

Replace App.js with this content:

import React from "react";

export const App = () => {
  return <>
    <h1>Hello, hello, hello</h1>
  </>
}

export default App;

Change title and description in public/index.html and remove unnecessary comments. The final result should look like this:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <meta name="theme-color" content="#000000" />
    <meta
      name="description"
      content="React router tutorial"
    />
    <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />

    <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
    <title>React Router example</title>
  </head>
  <body>
    <noscript>You need to enable JavaScript to run this app.</noscript>
    <div id="root"></div>
  </body>
</html>

Changes after this: step: Cleanin’ Out My Closet

It’s not time for styling

Yeah, so let’s use React Bootstrap, which provides some components and styles to focus on writing React code. It is a good move, isn’t it?

$ npm install react-bootstrap bootstrap@5.1.3

Import bootstrap styles in the index.js. Just add this line after other imports:

import 'bootstrap/dist/css/bootstrap.min.css';

OK, that all was easy. Let do something less trivial than importing things from npm.

npm developer

Changes after this: step: It’s not time for styling

Add Router component

To get React Router working in your App, you need to add a Router. Basically, it means that you need to wrap your app with a top-level router that makes all other React Router components and hooks work. A router is stateful, and it creates history with the initial location and subscribes to the URL.

React Router can subscribe to the URL changes thanks to the History object. Each user action that changes URL is kept in History Stack.

There are three types of those actions: PUSHPOP, and REPLACE.

  • PUSH – a new entry is added to the history stack
  • POP – it happens when a user click Browser’s back or forward buttons
  • REPLACE – Replace action is similar to PUSH, but it replaces the current entry in the history stack instead of adding a new one

A location is an object built on top of a window. location object. In this object, you can find information about URL, and in general, it represents where a user is at the time.

There are three types of Routers in react-router-dom:

  • BrowserRouter – recommended for running React Router in a Web browser
  • HashRouter – is used for apps where the URL should not be sent to a server for some reason. It’s not recommended to use the Hash router unless you absolutely have to.
  • MemoryRouter – the common case of using MemoryRouter is testing. It stores all information in an array.

OK, so let’s wrap our app by BrowserRouter.

index.js:

import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter } from "react-router-dom";
import App from './App';
import 'bootstrap/dist/css/bootstrap.min.css';

ReactDOM.render(
    <BrowserRouter>
        <App />
    </BrowserRouter>,
    document.getElementById('root')
);

Changes after this: step: Add Router component

Update app component (App.js) file with this content:

import React from "react";
import { NavLink } from "react-router-dom";
import { Navbar, Container, Nav } from 'react-bootstrap'

export const App = () => {
  return <>
    <Navbar bg="light" expand="lg">
      <Container>
        <Navbar.Brand>
          <NavLink to="/" style={{textDecoration: 'none', color: 'inherit'}}>Your account</NavLink>
        </Navbar.Brand>
        <Navbar.Toggle aria-controls="basic-navbar-nav" />
        <Navbar.Collapse id="basic-navbar-nav">
          <Nav className="me-auto">
            <NavLink to="/address" className="nav-link">Address book</NavLink>
            <NavLink to="/orders" className="nav-link">Orders</NavLink>
          </Nav>
        </Navbar.Collapse>
      </Container>
    </Navbar>
    <Container className="mt-3">
      <h1>Hello, hello, hello</h1>
    </Container>
  </>
}

export default App;

We imported a NavLink component here from react-router-dom:

import { NavLink } from "react-router-dom";

NavLink component is a special type of the Link component with an additional feature. It can have an “active” class where the URL is the same as “to property.

A Link React component renders <a> tag with real href property. The difference about the real <a> tag is that React Router will handle navigation to specific locations when you use Link (and NavLink).

So in our app, even we haven’t declared any routes yet, the URL is changed without page reloading thanks to React Router.

Besides, we imported there some Bootstrap stuff:

import { Navbar, Container, Nav } from 'react-bootstrap'

The page should look like this:

react router tutorial

Summarizing, we added three links: HomePage: /, Address book: /address, and Orders: /orders.

Changes after this: step: Add navigation and links

Add the first route

React Router is a declarative routing framework that means you configure Routes to use standard React components.

At the end of this step, you now have a react application to find navigation links showing the components for each route.

Let’s implement the first route. To do so, we need just to import Route and Routes components in the app and use them in this way:

import { Routes, BrowserRouter, Route} from "react-router-dom";

(...)

<BrowserRouter>
    <Routes>
        <Route path="/" element={<App />} />
    </Routes>
</BrowserRouter>

So we have a route that handles the “/” path and renders the App, a React component. Basically, nothing has changed in our app so far. Let’s implement other routes.

Changes after this step: Add the first route

Add first nested routes and outlet

React Router uses nested routes to provide the most detailed routing details inside child components. Those routes group your routing information directly into components to render other components.

By the finish of this step, you will have various ways of providing info. This is a little additional code, but the routes keep the child’s parents in line. Not every project uses a nested route: some prefer an explicit list.

Nested routes allow you to build a complex system of routing. Each route defines a portion of the URL through segments, and a single URL can match multiple routes. Take a look:

Here is our main route:

/

Here is the route for the address book:

/address

And here is the route for address details

/address/:addressId

So the route is built by three routes: / + address/ + :/addressId

Let’s implement that scenario. Please, replace <Route path=”/” element={<App />} /> by:

<Route path="/" element={<App />} >
    <Route path="address" element={<AddressBook />}>
        <Route path=":addressId" element={<AddressDetails />} />
    </Route>
</Route>

Hero you go!

Of course, we need to define two new components: AddressBook, and AddressDetails

src/routes/AddressBook/addressBook.js:

import React from 'react'

export const addressBook = () => {
    return <p>Address book will be here</p>
}

export default addressBook;

src/routes/AddressBook/index.js

export { default } from './addressBook';

Do the same for address details (and do not forget about importing these routes in index.js!)

That should work, but wait. If you go now for the address page, you will see that the address route is rendered, but it looks pretty the same as the index route, but we except that there will be a paragraph: Address book will be here.

path string

To render the content of any child, you need to use the Outlet component that renders the next match in a set of matches.

Please import the Outlet React component to the App component:

import { NavLink, Outlet } from "react-router-dom";

and add it below the <h1>

<Container className="mt-3">
  <h1>Hello, hello, hello</h1>
  <Outlet/>
</Container>

Now the paragraph from the address book component is in place

react application

Changes after this: step: Add first nested routes and Outlet

Add Index routes

Let’s go ahead and add some content to the address book. First, add some addresses:

const addresses = [
    {
        id: 1,
        addressName: 'Polna 1, Wrocław'
    },
    {
        id: 2,
        addressName: 'Wrocławska 2, Warszawa'
    }
];

Then, render navigation with addresses:

const navLinks = addresses.map(address => {
    return <ListGroupItem key={address.id}>
        <NavLink to={`/address/${address.id}`} key={address.id}>{address.addressName}</NavLink>
    </ListGroupItem>
});

const shouldDisplayNav = navLinks && navLinks.length ? <ListGroup>{navLinks}</ListGroup> : <p>There are no addresses.</p>;

Return all stuff with a nice layout:

return addresses ? <Row>
    <Col sm="3">
        {shouldDisplayNav}
    </Col>
    <Col sm="9">
        <Outlet/>
    </Col>
</Row> : <Row>
    <p>There are no addresses.</p>
</Row>

Do not forget about imports:

import { NavLink, Outlet } from "react-router-dom";
import { ListGroupItem, ListGroup, Col, Row } from "react-bootstrap";

The page should look like this:

react router - create nested routes

On the right of navigation, there is a space for address details, but initially then is empty space. When you click on an address in navigation, you can see address details.

There is a way to add some improvements! Let’s add a paragraph that says: “Please select an address.” To do so that you can use another pretty cool feature of React Router called: Index route

Add this code to index.js to AddressBook route component:

<Route
    index
    element={
        <p>Select an address.</p>
    }
/>

Now, when you go to the address book, you can see “Select an address” text by default:

react router - create nested routes

Let’s do something similar for the home route:

<Route
    index
    element={
        <>
            <h2>Welcome in your account.</h2>
            <p>Please use the navigation above to see Address book or your orders.</p>
        </>
    }
/>

Remove this code from App.js

<h1>Hello, hello, hello</h1>

The homepage looks like this now:

how yo build dynamic routes with react router

Changes after this: step: Add index routes

Use URL params

We have already defined a route for Address Details that receives addressId param:

<Route path=":addressId" element={<AddressDetails />} />

When you click on addresses, the URL is changing:

http://localhost:3000/address/1
http://localhost:3000/address/2

“1” and “2” in this case are addresses ID. The question is: how do we handle those params in the AddressDetails React component?

useParams hook

React Router provides a useParams hook that allows you to handle URL params. Take a look:

import React from 'react'
import { useParams } from "react-router-dom";

export const AddressDetails = () => {
    const { addressId } = useParams();
    return <p>Address details for {addressId} will be here</p>
}

export default AddressDetails;

Now, addressId is handled by the AddressDetails component:

how yo build dynamic routes with react router

Changes after this step: Use URL Params

Use search params

React Routes provides a useSearchParams hook that allows you to read and modify a query part of a URL (q=). Let’s use it to add some filtering to the App.

First import useSearchParams hook in the AddressBook React component ad get searchParams, and setSearchParams from it:

import { useSearchParams } from "react-router-dom";

// below in the compoonent body:

const [searchParams, setSearchParams] = useSearchParams();

Second, add a search form. To do so, add this code at the beginning of the return function:

<Col sm="12">
    <nav>
        <InputGroup size="sm" className="mb-3">
            <InputGroup.Text id="address-search">Search for an address</InputGroup.Text>
            <FormControl aria-label="Search for an address"
                         aria-describedby="address-search"
                         value={searchParams.get("filter") || ""}
                         onChange={event => {
                             const filter = event.target.value;
                             if (filter) {
                                 setSearchParams({ filter });
                             } else {
                                 setSearchParams({});
                             }
                         }} />
        </InputGroup>
    </nav>
</Col>

A function bound on the onCahnge event sets the current input value to the URL query param.

Third, let’s read the query param and filter addresses by it:

const navLinks = addresses
    .filter(address => {
        const filter = searchParams.get('filter');
        if (!filter) return true;

        let name = address.addressName.toLowerCase();

        return name.startsWith(filter.toLowerCase());
    })
    .map(address => {
        return <ListGroupItem key={address.id}>
            <NavLink to={`/address/${address.id}`} key={address.id}>{address.addressName}</NavLink>
        </ListGroupItem>
});

In the previous step, we named a param by word: filter, and now we can read that value by using this: searchParams.get(‘filter’);

Take a look at the filtering in action:

react router - path parameters - search params

Changes after this step: Use search params

Handle no matching route

The last thing I want to show you is the no-match route. It’s a case when the user goes to a route that does not exist, for example,/blablabla

To handle that, add this route component definition at the end of your route components definitions:

<Route
    path="*"
    element={
        <main>
            <p style={{padding: '30px', textAlign: 'center'}}>There's nothing here!</p>
        </main>
    }
/>

That code handles all routers not handled by other defined routes components. On the other hand, if no routes match, those elements will be rendered. Of course, you can use the react component as well.

Changes after this step: Handle no matching route

An additional thing: protected Routes

A protected route is used to ensure only logged-in users can use some places on your site. Typically we create e a secure route component for someone in the system to use /admin when they attempt to connect. However, some aspects of React Router must first be covered.

Basically, you can create a special React component that will check if a user can go to a protected route or not.

Working Demo

Here you can see the demo of the application we developed with react-router:

react-router-tutorial-omega.vercel.app

Source code

Here you can find the source code for this tutorial: https://github.com/Frodigo/react-router-tutorial

Here are the commits for each step:

  1. Scaffold the project
  2. Setting up React Router
  3. Cleanin’ Out My Closet
  4. It’s not time for styling
  5. Add Router component
  6. Add navigation and links
  7. Add the first route
  8. Add first nested routes and Outlet
  9. Add index routes
  10. Use URL Params
  11. Use search params
  12. Handle no matching route

Summary

React Router lets you handle all the routes in a React application. You can use it for a web app or even for React native app.

The router is one of the main React Router components, and for web apps, there is a BrowserRouter react component, a router implementation that uses HTML5 History API.

React Router provides other essential components are Routes, Route, Link, and NavLink.

Besides, there are a few hooks like useParams, useSearchParams, and useNavigate.

So react-router package provides just components and just hooks, and those all together allow you to create complex routing systems easily.

Share the Post:

You might also like

Mastering the Build Measure Learn Cycle for Optimal Product Evolution

Mastering the Build Measure Learn Cycle for Optimal Product Evolution

In a world where businesses must adapt quickly to stay competitive, mastering the Build Measure Learn cycle is invaluable for product development and innovation. This methodology, rooted in the lean startup approach, prioritizes rapid iteration, MVPs, and data-driven decision-making, providing a solid foundation for building successful products that meet customer needs and exceed expectations.

Read More

Lean Startup: A Different Approach to Business Development

Would you like your business to grow as fast as Kylian Mbappe runs after the ball? What can be done to ensure that introducing products to the market and adapting them to customer needs is as fast as possible to overtake the competition? I invite you to learn about the Lean startup methodology.

Read More