If you don’t know what Routing is, it’s how a web application directs traffic. React doesn’t come with a built-in router so we’ll be using React Router which is a popular routing library for React.

Goals

This tutorial aims at helping beginner react developers understand and implement basic routing in a React application using React Router. We’ll be building a simple application which connects to the GitHub API to fetch information about users and repositories. The app will have 3 screens:

  1. Home page - The applications starting point
  2. User page - A page for presenting information about a GitHub user
  3. Repository page - A page with information about a repository

You can find the source code below if you get lost during the tutorial. In the source code, you can find a style.css file which is used in the tutorial to add some basic styling.

Prerequisites

This tutorial assumes basic knowledge of HTML, CSS, and JavaScript/ES6. If you’re new to React, start by learning the fundamentals Getting Started with React.

In the tutorial we’ll be fetching information from an API and will not cover the basics of this process. If you don’t have any previous experience working with external APIs, I recommend that you head over to Creating A Live News App Using React.

React Router

Before we dig into the actual coding, we’ll start with some fundamentals of React Router. As of v4, React Router uses something called Dynamic Routing. This means that the routing will take place when the app is being rendered. In comparison, Static Routing requires declaration of routes as part of the initialization of the app before any rendering takes place.

With dynamic routing, there’s no need for any configuration outside the app. This means that almost everything in React Router is a component. Let’s visualize how this works with an example.

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

function App() {
  return <h1>Basic React Router App</h1>;
}

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

Notice how we wrap the App component inside a React Router component called BrowserRouter, after we call the render() method.

React Router Components

BrowserRouter is just one of many React Router components. There are three primary components that we’ll be using.

  1. Routers - To use React Router, we need to wrap the entire app within a router component. BrowserRouter is one of two routers which can be used.
  2. Route Matchers - These components are used to match the current urls and return the matching component.
  3. Navigation (also Route Changers) - React Router provides components for navigation. The components will render an anchor <a> in the HTML document when used.

Getting Started

We’ll start by creating a new React app.

npx create-react-app react-router-news-app

All components from React Router should be imported from react-router-dom, so let’s install it as well.

npm install react-router-dom

Then we need to wrap our app within a router component. There are two types of routers:

  • BrowserRouter - creates the best-looking URLs, e.g. website.com/contact. This requires correct configuration of the server in order to work.
  • HashRouter - creates URLs using a #, e.g. website.com/#contact. The hash (#) is used to store the current location and is never sent to the server. Therefore no configuration is required.

BrowserRouter comes prefigured for us when using create-react-app, so this is what we’ll be using.

Inside index.js we’ll import react-router-dom and wrap our app inside a router.

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

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

Route Matchers

The next step is to decide on the routes we want to use within our app and direct the traffic accordingly. This logic will take place inside the App.js file of our project.

There are two route matching components:

  • Switch - is used to group your routes, and renders the first one (starting from the top) that matches the current URL.
  • Route - is a child of the Switch component and corresponds to each individual route.

Inside App.js, let’s add both the Switch component and a Route for each page. This requires that we have a component for each page, we’ll implement these later.

import React from "react";
import { Route, Switch } from "react-router-dom";
// Pages
import HomePage from "./pages/homePage";
import UserPage from "./pages/userPage";

export default function App() {
  return (
    <Switch>
      <Route exact path="/" component={HomePage} />
      <Route path="/:id" component={UserPage} />
    </Switch>
  );
}

Here, we’re matching the root route / to our HomePage component. A Route with path="/" will always match a URL because all URLs begin with a /. Therefore we need to add exact, which ensures there’s a match only if the URL exactly matches the path. We’re dynamically matching the rest to UserPage.

This is a simple example, but a more complex example could look like.

<Switch>
  <Route exact path="/" component={HomePage} />
  <Route path="/categories" component={CategoriesPage} />
  <Route path="/categories/:id" component={CategoryPage} />
  <Route path="/:id" component={UserPage} />
</Switch>

For the above example, the /categories path would return a page with all the categories whereas the /categories/javascript would go to a page with the specific category. Remember, once the Switch component finds a match it will render the component and ignore the rest. Therefore we need to put the more specific routes before the less specific ones.

In order to navigate within the application, we’ll be using the Link component. There’s also a special type of Link, called NavLink, which can be styled when active.

In order to use the Link component, we’ll need to create our page components. Inside /src create a folder /pages where we’ll store the page components. We’ll start with the home page of the app, create a file inside the folder called homePage.js.

import React from "react";
import { Link } from "react-router-dom";
import "../style.css";

export default function HomePage() {
  return (
    <div className="container">
      <h1>Home Page</h1>
      <p>
        View <Link to="/facebook">facebook</Link> on GitHub.
      </p>
    </div>
  );
}

If we start the development server and go to the browser, we can see that the app is matching the root route / and rendering the HomePage with the title and the link.

router home page

Dynamic Routes

When we click on the facebook link in our home page, the Link will navigate to /facebook which will match the Route with the path /:id. We’ll have to add the page UserPage in which we dynamically fetch the user profile from GitHub based on the URL.

In order to do this, we’ll use the match object which contains information about how the Route matched the URL. More specifically, we’ll use match.params.id which is accessible through the props.

Inside the /pages folder, create a new file userPage.js.

import React, { Component } from "react";
import "../style.css";

export default class UserPage extends Component {
  state = {
    user: {},
    loading: true,
  };

  componentDidMount = async () => {
    try {
      // Pass the param :id to the API call
      const response = await fetch(
        `https://api.github.com/users/${this.props.match.params.id}`
      );
      // read and parse the data using json() to create a JavaScript object
      const user = await response.json();
      this.setState({ user, loading: false });
    } catch (error) {
      // We're not going to focus on handling the error in this part in this tutorial.
      this.setState({ loading: false });
    }
  };

  render() {
    const { loading, user } = this.state;
    return (
      <div className="container">
        {loading ? (
          <p>Loading...</p>
        ) : (
          <div className="bio">
            <img src={user.avatar_url} alt={user.username} />
            <div>
              <h1>{user.name}</h1>
              <p>{user.bio}</p>
              <div className="bio-details">
                <p>{user.location}</p>
                <a href={user.blog}>{user.blog}</a>
              </div>
            </div>
          </div>
        )}
      </div>
    );
  }
}

When we click on facebook from our home screen, we will see the information about the Facebook user from GitHub.

router user page

Nested Routes

Let’s give our app some more functionality by displaying the users repositories as well. We’ll want to be able to navigate to a new page to see the information about each specific repository created by the specific user.

Let’s begin by adding the repos created by the user. We’ll add a new function fetchRepos() inside userPage.js. Then function will be responsible for fetching the users repos.

componentDidMount = async () => {
  try {
    // Pass the param :id to the API call
    const response = await fetch(
      `https://api.github.com/users/${this.props.match.params.id}`
    );
    // read and parse the data using json() to create a JavaScript object
    const user = await response.json();
    // update the state with the user object
    this.setState({ user });
    // fetch the users repos
    const userRepos = await this.fetchRepos();    this.setState({ userRepos, loading: false });  } catch (error) {
    // We're not going to focus on handling the error in this part in this tutorial.
    this.setState({ loading: false });
  }
};

// The function fetches users repo sorted by the most recent updated ones.
fetchRepos = async () => {
  const response = await fetch(
    `https://api.github.com/users/${this.props.match.params.id}/repos?sort=updated`
  );
  const json = await response.json();
  // Only return the 10 most recent updated
  return json.slice(0, 10);
};

Then we’ll add a <ul> which renders a list of the repos.

{
  !loading && userRepos.length > 0 && (
    <div className="repos">
      <h3>Repositories</h3>
      <ul>
        {userRepos.map((repo) => (
          <li key={repo.id}>
            <p>
              <Link
                to={{
                  pathname: `${this.props.match.params.id}/${repo.name}`,
                  state: { repo },
                }}
              >
                {repo.name}
              </Link>{" "}
              - {repo.description}
            </p>
          </li>
        ))}
      </ul>
    </div>
  );
}

Each repo will have a Link with a nested url navigating to /user/repo. If we navigate to /facebook, we should be able to see the 10 latest updated repos belonging the facebook user.

facebook-repos

When we click on one of the repos, we want to be taken to a screen with information about the repository and with the url facebook/repo-name. To achieve this functionality, we’ll be using nested routes. However, remember that almost everything in React Router is components. To create nested routes we’ll basically just created a nested Route component inside the UserPage component.

Inside the userPage.js we’ll wrap the code we’ve previously written inside a Route component which itself is inside a Switch component, just like we did inside the App.js file. The render() function should now look like this.

render() {
  const { loading, user, userRepos } = this.state;
  return (
    <Switch>
      <Route exact path={this.props.match.path}>
        // The code for the UserPage component
      </Route>
      <Route path={`${this.props.match.path}/:repoId`} component={RepoPage} />
    </Switch>
  );
}

Don’t forget to import Switch, Route and RepoPage.

Let’s create the RepoPage component inside the /pages folder.

import React from "react";

export default function RepoPage(props) {
  return <div>Repo Page</div>;
}

Nothing fancy going on here, but we want to to be able to list information about the specific repository. We could make a new request to the GitHub API, but we already have all the information from when we fetched the repos inside the UserPage component.

To access this information inside the RepoPage component, we’ll need to pass down the repo object. This is done using the to prop inside Link. We’ll need to change the value of the prop to an object with two keys: pathname to the page we link to and state with the repo object.

<ul>
  {userRepos.map((repo) => (
    <li key={repo.id}>
      <p>
        <Link
          to={{            pathname: `${this.props.match.params.id}/${repo.name}`,            state: { repo },          }}        >
          {repo.name}
        </Link>{" "}
        - {repo.description}
      </p>
    </li>
  ))}
</ul>

Now we can access the repo inside RepoPage through props.location.state.repo which we use to to add some information about the repository. Let’s also add a button to navigate back to the UserPage. We’ll use the goBack() method through props.history.goBack.

export default function RepoPage(props) {
  const { repo } = props.location.state;
  return (
    <div className="container">
      <h1>
        <a href={repo.html_url}>{props.match.params.repoId}</a>
      </h1>
      <p>{repo.description}</p>
      <ul className="repo-facts">
        <li>
          <b>Stars</b> - {repo.watchers}
        </li>
        <li>
          <b>Forks</b> - {repo.forks}
        </li>
        <li>
          <b>Open issues</b> - {repo.open_issues}
        </li>
      </ul>
      <button onClick={() => props.history.goBack()}>Go back</button>
    </div>
  );
}

Let’s test it out by opening the react repo.

react-repo

Adding 404 Page

Before we wrap things up, let’s add a 404 page which a user will be directed to when no routes match the current URL. Create a new page notFoundPage.js inside the pages folder.

import React from "react";
import "../style.css";

export default function NotFoundPage() {
  return <div className="container">404 - Page not found.</div>;
}

Now we need to add a Route component for the page inside App.js. We’ll place this in the bottom of the Switch component.

import NotFoundPage from "./pages/notFoundPage";

<Switch>
  <Route exact path="/" component={HomePage} />
  <Route path="/:id" component={UserPage} />
  <Route component={NotFoundPage} /></Switch>;

Wrapping up

Adding routing to your React app is not hard once you’ve understood the logic. Hopefully this tutorial helped you with this process so that you can start using React Router in your projects. If you got lost on the way, you can find the source below.