Single Page Applications (SPAs) have revolutionized web development. They offer a more dynamic and fluid user experience compared to traditional multi-page applications.

Traditional web apps require full-page reloads for almost every click the user makes. SPAs, on the other hand, load a single HTML page and update the page contents dynamically as users interact with the application. This dynamism mimics the feel of desktop applications and results in better responsive interactions.

Before moving any further:

To follow along with what I'll discuss in this article, you should have some basic knowledge of React and how to set up a React project. If you already do, let's crack on.

What are React Router and React Router DOM?

React router is a powerful library that manages navigation and routing in React applications. The React Router DOM is used specifically for web applications and has few DOM-specific APIs.

As we dive into the world of React Router DOM, we'll explore its core concepts while demonstrating their implementation in a React application. Our focus will be on building a simple navigation system with links to different components, illustrating how to configure routes, handle route matching, and implement navigation.

By the end of this article, you'll have a solid understanding of how to use React Router DOM to create dynamic and seamless navigation experiences in your single-page applications.

How to Install React Router

As I explained above, React-router-DOM is used exclusively for integrating routing functionalities into web applications. So, in order to use it in your React app, you need to install the react-router-dom package by running this command in your React app terminal:

npm install react-router-dom

After successfully installing it, you can now start routing in your React project.

Core Concepts in React Router DOM

BrowserRouter

BrowserRouter a parent component that houses all the route components. All routes that you use in an application must be declared within the BrowserRouter. Most importantly, it stores the current location in the browser's address bar using URLs which comes in handy during navigation.

To use the BrowserRouter, you'll need to import it from the react-router-dom in your App.jsx file.

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

function App() {

  return (
    <BrowserRouter>

    </BrowserRouter>
  );
}

export default App;

The BrowserRouter has a basename attribute used to set base URL for all routes in an application. It's important if your app is hosted in a subdirectory on a domain.

<BrowserRouter basename="/shop">

</BrowserRouter>

Adding /shop as a basename will make sure that all route paths are relative to /shop.

Routes

This component is a direct replacement for switch which was used in the former versions of React Router. It also acts like a parent and renders the first matching child route, which ensures that the correct component is displayed based on the current URL.

To declare routes, import routes from react-router-dom and position it within the BrowserRouter component.

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

function App() {

  return (
    <BrowserRouter>
        <Routes>

        </Routes>
    </BrowserRouter>
  );
}

export default App;

Route

Route a child component that consists of two attributes: path and element. A path can be any specified path name while the element attribute is the component that should be rendered. A route renders a specific component when the path specified matches a URL.

An application can have as many routes as it needs, and they must all be declared inside the Routes component. Assuming we have a <Home\> and <Pricing\> component, we will have to import the Route component and position it within the Routes.

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

//ALL COMPONENTS IMPORTS COMES HERE

function App() {

  return (
    <BrowserRouter>
        <Routes>
            <Route path="/" element={<Home/>}/>
                <Route path="pricing" element={<Pricing/>}/>
        </Routes>
    </BrowserRouter>
  );
}

export default App;

Undeclared Routes

There is a way to handle routes that do not exist in your application, just like an Error 404 page. To do this, create another component bearing a Not Found message and the route added.

Set the path name to * and pass the component as the element.

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

//ALL COMPONENTS IMPORTS COMES HERE

function App() {

  return (
    <BrowserRouter>
        <Routes>
            <Route path="/" element={<Home/>}/>
                <Route path="pricing" element={<Pricing/>}/>
                <Route path="*" element={<PageNotFound/>}/>
        </Routes>
    </BrowserRouter>
  );
}

export default App;

Nested Routes

In some cases, routes can have children or sub-routes.

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

//ALL COMPONENTS IMPORTS COMES HERE

function App() {

  return (
    <BrowserRouter>
        <Routes>
            <Route path="/" element={<Home/>}/>
                <Route path="pricing" element={<Pricing/>}/>
                <Route path="categories" element={<Categories/>}>
                    <Route path="male" element={<Male/>}/>
                        <Route path="female" element={<Female/>}/>
                </Route>
                <Route path="*" element={<PageNotFound/>}/>
        </Routes>
    </BrowserRouter>
  );
}

export default App;

On navigating to the nested elements, the URL on the browser will be reading something like /categories/male and /categories/female.

This acts like an anchor href attribute. It has a to attribute which specifies where the Link will take the user after a click. Usually, it is the path to a component's page that is passed to the to attribute.

Links are typically put in a Navbar component, so we will put two Links that point to the components path in our already declared Routes.

import { Link } from "react-router-dom";
export default function PageNav() {
  return (
  <>
        <Link to="/">Home</Link>
        <Link to="pricing">Pricing</Link>
  </>
  );
}

NB: If you are practicing along while reading this article, it is important to note that the PageNav component created here should be situated in your App.jsx and specifically just after the opening BrowserRouter tag before the Routes. This is to ensure the PageNav always stays at the top like a navigation menu despite routing through different components.

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

//ALL COMPONENTS IMPORTS COMES HERE

function App() {

  return (
    <BrowserRouter>
        <PageNav/>
        <Routes>
            <Route path="/" element={<Home/>}/>
                <Route path="pricing" element={<Pricing/>}/>
                <Route path="categories" element={<Categories/>}>
                    <Route path="male" element={<Male/>}/>
                        <Route path="female" element={<Female/>}/>
                </Route>
                <Route path="*" element={<PageNotFound/>}/>
        </Routes>
    </BrowserRouter>
  );
}

export default App;

NavLink performs the same function as Link and has a to attribute as well. But it's different as it has a class attribute. The class attributes are active, isPending, and isTransitioning. This makes it more versatile than Link and you can use it to conditionally add styles during user interactions.

import { NavLink } from "react-router-dom";
export default function PageNav() {
  return (
      <>
        <NavLink to="/">Home</NavLink>
        <NavLink to="pricing">Pricing</NavLink>
    </>
  );
  }

Outlet

Having child elements inside a parent route element means there is a layer of abstraction in rendering the child routes' UI. This is where the Outlet component comes into play. You add it to the parent route – in our example, it would be the Categories component.

import { NavLink, Outlet } from "react-router-dom";
export default function Categories() {
  return (
 <>
          <NavLink to="men">
            Men
          </NavLink>
          <NavLink to="women">
            Women
          </NavLink>
      <Outlet />
</>
  );
}

This allows for the rendering of the child routes UI in the nested route.

Image Image showing children nested routes in Categories route

useNavigate Hook

This hook returns a function that enables programmatic navigation between routes.

There are several ways to use the navigate function in your application. First, we need to import the useNavigate hook and initialize it as navigate.

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

export default function Homepage() {
  const navigate = useNavigate();

  return (
    <>
      <h1>This is the Homepage</h1>


    </>
  );
}

We can use navigate in the following ways in our application:

  • Attaching it to a button via the onClick prop with the intended path to be navigated to, passed to the navigate function.
 <button onClick={() => navigate("/categories")}>Go to Categories</button>
  • Using it with a Link component.
<Link to={navigate("/categories")}>Go to Categories</Link>
  • Using a number instead of the component path in the navigate function. The number should specify the number of navigations backward in the history stack where you would like to go.
<Link to={navigate(-1)}>Go one step backwards</Link>

useParams Hook

Returns an object of the dynamic params gotten from the current URL matched by the Route's path. The parent routes pass all params to their child routes.

The example below shows an OrderPage component that will be rendered for every customer with their unique id. When the URL matches /customer/123, :id will be 123.

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

function App() {
  const {id} = useParams()
  return (
    <BrowserRouter>
        <Routes>
        <Route path="customer">
            <Route path=":id" element={<OrderPage/>}/>
           </Route>
        </Routes>
    </BrowserRouter>
  );
}

export default App;

Final Result

At this point, we've fully implemented React Router in our small navigation project. This is what it looks like in full flow:

Image

For more detailed information and concepts about React router, you can visit the Official React Router documentation site.

Conclusion

In this article, we've explored the concepts and implementation of Client Side Routing (CSR) in a React web application. React Router, through its web-centric library React-Router-DOM, enables CSR, allowing apps to update the URL with a click of a link without making a server request for a new document.

This functionality enhances the user experience by providing faster navigation and a more seamless interaction within the application. By leveraging CSR, developers can build more efficient and responsive single-page applications (SPAs), ultimately improving performance and user satisfaction.

If you enjoyed reading this article, you could Buy me a Coffee.

Want to see more of these? Connect with me on LinkedIn.

See you on the next one!

Happy Coding!