Gaurav Singhal Gaurav is a data scientist with a strong background in computer science and mathematics. As a developer, he works with Python, Java, Django, HTML, Struts, Hibernate, Vaadin, web scraping, Angular, and React.

Using Hooks with React Router

7 min read 1990

React Router Hooks

Editor’s note: This article was last updated 16 November 2022 to include changes made in React Router v6, including the removal of the deprecated useHistory Hook. 

React Hooks were introduced with the release of React v16.8.0 to much excitement. With Hooks, developers can write cleaner components with less boilerplate code in comparison to class components. Many popular React packages are adding support for Hooks so developers can leverage their APIs in functional components.

React Router, the go-to routing solution for React apps, added Hooks support in its v5.0 release. These React Router Hooks provide developers with new ways to handle the router state. In this tutorial, you’ll learn how to use Hooks with React Router and minimize the number of code lines in a component. Let’s get started!

How do Hooks work with React Router?

To demonstrate how Hooks work, we’ll create a React project and set up the pages. To create a new React app, run the following command:

npx create-react-app router-hooks-demo

router-hooks-demo is the app’s name, but you can name it whatever you want. Next, add the react-router-dom package:

npm i react-router-dom --save

Import the BrowserRouter, Link, Route, and Routes components from the react-router-dom package. We’ll use these components to configure client-side routing in our React app:

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

For this demo, you’ll only have two routes or pages, the Home page and the User page. We’ll mainly be working on the User page:

const User = () => {
  return <div>This is the user page</div>;
};

const Home = () => {
  return <div>This is the home page</div>;
};

In the App component, create a navigation bar and add hyperlinks to the Home and User pages using React Router’s Link component. On the webpage, the Link component is rendered as an <a> tag:

<nav>
  <div>
    <Link to="/">Home</Link>
  </div>
  <div>
    <Link to="/user/:id">User</Link>
  </div>
</nav>

Next, add the Routes and Route components, then wrap everything in the BrowserRouter component:

<BrowserRouter>
  <nav>
    <div>
      <Link to="/">Home</Link>
    </div>
    <div>
      <Link to="/user/:id">User</Link>
    </div>
  </nav>
  <Switch>
    <Route path="/" element={<Home />} />
    <Route path="/user/:id" element={<User />} />
  </Switch>
</BrowserRouter>

The BrowserRouter component enables the client-side routing feature and handles the routing logic using the browser history API. The Route component renders the page UI when the route path matches the active URL. The Routes component’s job is to understand its children Route elements and decide which one to render when a user navigates to different routes.

The final App.js file should look like the following:

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

const User = () => {
  return <div>This is the user page</div>;
};

const Home = () => {
  return <div>This is the home page</div>;
};

export default function App() {
  return (
    <div className="App">
      <BrowserRouter>
        <nav>
          <div>
            <Link to="/">Home</Link>
          </div>
          <div>
            <Link to="/user/:id">User</Link>
          </div>
        </nav>
        <Routes>
          <Route path="/" element={<Home />} />
          <Route path="/user/:id" element={<User />} />
        </Routes>
      </BrowserRouter>
    </div>
  );
}

Why are React Router Hooks useful?

Let’s say you need to access the current pathname of the URL inside of a page component. Before Hooks, you would have to pass the page component to the component prop of the Route component. Then, the Route component would inject with route props, match, location, and history.

Although this approach works, the component becomes harder to read, and it’s difficult to understand how the props are injected when it comes to maintaining the project. The React Router authors added Hooks support so that the page components can access history, location, and match objects without having to pass the page component as a component prop in the Route component:

// Route with component prop
<Route path="/user/:id" component={User} />;

// User component
const User = (props) => {
  const location = props.location;

  console.log(location.pathname);

  return <div>This is the user page</div>;
};

React Router Hooks

Now that you understand why we add Hooks for routing, let’s see the Hooks in action.

useParams

The useParams Hook returns an object containing key-value pairs of any parameters that exist in a URL. For example, let’s say you have a User page component that accepts an id as a parameter in the URL. You can access the parameter using the useParams Hook:

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

<Route path="/user/:id" element={<User />} />

const User = () => {
  const params = useParams();

  console.log(params);

  return (
    // ...
  )
}

If you were to pass 234 as the ID in the user URL, /user/234, the useParams() Hook would return the following object:

{
  id: 234,
}

You can display the id parameter on the page or use it to fetch the user’s information from the server:

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

// /user/:id
const User = () => {
  const params = useParams();

  return (
    <div>
      <p>This is the user page</p>
      <p>current user Id - {params.id}</p>
    </div>
  );
};

useNavigate

In React Router v6, the useHistory Hook has been discontinued, providing useNavigate as its alternative. The main reason for the switch from useHistory to useNavigate is to provide better compatibility with React Suspense.

The useNavigate Hook returns a function that lets you handle route changes and navigate programmatically:

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

const User = () => {
  let navigate = useNavigate();
  const params = useParams();

  const handleBack = () => {
    navigate(-1);
  };

  const handleNavigation = () => {
    navigate("/user/5");
  };

  return (
    <div>
      <div>This is the user page</div>
      <div>current user Id - {params.id}</div>
      <div>
        <button onClick={handleBack}>Go Back</button>
      </div>
      <div>
        <button onClick={handleNavigation}>Go To Different User</button>
      </div>
    </div>
  );
};

In the code block above, we add two buttons to the User page component. One button takes the user to the previous page, and another navigates the user to a different page. The navigate(-1) method is equivalent to clicking on the back button. It navigates the user to the previous page and goes back one entry in the history stack.

useLocation

The useLocation Hook allows you to access the location object that represents the active URL. The value of the location object changes whenever the user navigates to a new URL. The useLocation Hook can be convenient when you have to trigger any event whenever the URL changes.

Consider that you have to keep track of views on users’ profile pages. You can detect changes in the location object using the useEffect Hook, which comes with React:

import { useNavigate, useParams, useLocation } from "react-router-dom";

const User = () => {
  const navigate = useNavigate();
  const params = useParams();
  const location = useLocation();

  useEffect(() => {
    console.log(location.pathname);
    // Send request to your server to increment page view count
  }, [location]);

  const handleBack = () => {
    navigate(-1);
  };

  const handleNavigation = () => {
    navigate("/user/5");
  };

  return (
    <div>
      <div>This is the user page</div>
      <div>current user Id - {params.id}</div>
      <div>
        <button onClick={handleBack}>Go Back</button>
      </div>
      <div>
        <button onClick={handleNavigation}>Go To Different User</button>
      </div>
    </div>
  );
};

useMatch

React Router v6 has discontinued the useRouteMatch Hook, providing the useMatch Hook as its replacement. useMatch returns the match data about a route relative to the current route. It matches the active URL with a given path, similar to how the Route component works.

What problem does the useMatch hook solve? Say you had a /blog route where you display multiple blog posts and a blog/:id route where you display the details of the individual blog posts:

const App = () => {
  [const posts, setPosts] = useStatte([....])

  return (  
    <Routes>
      <Route path="/blog/:id" element={<Post post={post} />} />        
      <Route path="/blog" element={<Posts posts={posts} />} />   
      <Route path="/" element={<Home />} />      
    </Routes>   
  )
}  

Previously, you would have to pass all the posts to the Post component and filter out the specific post using the useParams Hook:

const Post = ({ posts }) => { 
  const id = useParams().id
  const post = posts.find(post => post.id === Number(id))

  return (
    <div>
      <span>{post.user}</span>
      <p>{post.content}</p>
    </div>
  )
}

While this approach works, we can leverage the useMatch Hook to create a cleaner implementation. The Post component receives only the data it should display, and not the array of posts.

Use the useMatch Hook in the app component:

import { useMatch } from "react-router-dom"

const App = () => {
  [const posts, setPosts] = useStatte([....])

  const match = useMatch('/blog/:id')  
  const post = match
  ? posts.find(post => post.id === Number(match.params.id))    
  : null

  return (  
    <Routes>
      <Route path="/blog/:id" element={<Post post={post} />} />        
      <Route path="/blog" element={<Posts posts={posts} />} />   
      <Route path="/" element={<Home />} />      
    </Routes>   
  )
}  

With this implementation, every time the URL changes, the command below is executed to check if the current route matches the desired URL:

const match = useMatch('/posts/:id')

If the URL matches, the match variable will return an object containing data about the matching URL. You can then access the id parameter from the object, use it to filter out the correct post data, and pass it to the Post component:

const Post = ({ post }) => { 
  return (
    <div>
      <span>{post.user}</span>
      <p>{post.content}</p>
    </div>
  )
}

useRoutes

React Router provides two methods for defining routes in React applications. The first and most popular is using the <Routes> and <Route> component. The second approach involves using the useRoutes Hook and plain JavaScript objects to declare your routes.

To see how the useRoutes Hook works, start by creating a routes.js file where you’ll create the routes:

import { useRoutes } from "react-router-dom";
import Home from "./pages/Home";
import Post from "./pages/Post";
import Posts from "./pages/Posts";
import User from "./pages/User";

export default function Router() {
  let element = useRoutes([
    { path: "/home", element: <Home /> },
    { path: "/posts", element: <Post /> },
    { path: "/post:id", element: <Posts /> },
    { path: "user", element: <User /> },
  ]);

  return element;
}

Then, import the routes into your App.js file:

import { BrowserRouter } from "react-router-dom";
import Router from "./routes";

export default function App() {
  return (
    <BrowserRouter>
      <div className="App">
        <h1>useRoutes Example</h1>
        <Router />
      </div>
    </BrowserRouter>
  );
}

There are no benefits of using one method for defining routing over another. It boils down to the preferences and patterns you and your team put in place.

Conclusion

Hooks are a great addition to the React ecosystem. In this article, we reviewed some code for handling routing with Hooks, using the useParams, useNavigate, useLocation, useMatch, and useRoutes Hooks. Now, you’re ready to take full advantage of what they have to offer.

Get setup with LogRocket's modern React error tracking in minutes:

  1. Visit https://logrocket.com/signup/ to get an app ID.
  2. Install LogRocket via NPM or script tag. LogRocket.init() must be called client-side, not server-side.
  3. $ npm i --save logrocket 

    // Code:

    import LogRocket from 'logrocket';
    LogRocket.init('app/id');
    Add to your HTML:

    <script src="https://cdn.lr-ingest.com/LogRocket.min.js"></script>
    <script>window.LogRocket && window.LogRocket.init('app/id');</script>
  4. (Optional) Install plugins for deeper integrations with your stack:
    • Redux middleware
    • ngrx middleware
    • Vuex plugin
Get started now
Gaurav Singhal Gaurav is a data scientist with a strong background in computer science and mathematics. As a developer, he works with Python, Java, Django, HTML, Struts, Hibernate, Vaadin, web scraping, Angular, and React.

2 Replies to “Using Hooks with React Router”

  1. I was following this as a codealong, start is a bit confusing maybe add that the changes are made in App.js.
    But I get a compile error at the useEffect() part, until I realized I still had to import it from react.
    I’m just 2 weeks in coding in React, so guess for a beginner they will need some more guidance

  2. Did they change something in the ‘react-router-dom’ npm? because it says Switch is not exported from ‘react-router-dom’

Leave a Reply