Accessibility-focused routing in React with Reach Router

Accessibility-focused routing in React with Reach Router


As you may already know, a Single Page Application is a type of application that has only one page and hydrates that page with new content upon user interaction.

Libraries or Frameworks like React allow you to build Single Page Applications and they do so with the help of a router.

A popular router for React is the React Router which currently has about 33.8k stars on GitHub. A newer router is Reach Router which was authored by a co-author of React Router, Ryan Florence.

React Router or Reach Router?

I’d say the main difference between React Router and Reach Router is that Reach Router comes with accessibility benefits such as focus management built-in. Also, Reach Router looks more elegant, and it makes things like Nested Routing easier to write.

Getting started with Reach Router

Installation

Create a new React project and install Reach Router.

To install Reach Router via:

npm

npm install @reach/router

Yarn

yarn add @reach/router

Basic Routing

Now that we have Reach Router installed, let us do some basic routing.

We are going to create two components – a Home component and a Dashboard Component. We will then use Reach Router to “switch” between the two components. Let’s write our two components.

Note: I will be writing all my code in my App.js

//App.js

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

const Dashboard = () => {
  return (
    <div>
      <h1>Dashboard</h1>
    </div>
  );
};

Now that we have our components ready, we can start routing. To “switch” between the two components, we need links to point to each component and a Router component of which these two components will be children. We will start with the link. For routing, we need to use a Link component which will be imported from Reach Router. Each Link component will have a to attribute which is the path of the component (we will define this path soon). Let’s add our links.

import { Link } from "@reach/router";

function App() {
  return (
    <>
      <div style={{ display: "flex", justifyContent: "space-evenly" }}>
        <Link to="/">Home</Link>
        <Link to="dashboard">Dashboard</Link>
      </div>
    </>
  );
}

As you can see, the path to the home component is “/” which means the home component will be rendered when the user is on the root path.

Now that we have the links, let us import the Router component and use it.

import { Link, Router } from "@reach/router";

function App() {
  return (
    <>
      <div style={{ display: "flex", justifyContent: "space-evenly" }}>
        <Link to="/">Home</Link>
        <Link to="dashboard">Dashboard</Link>
      </div>
      <Router>
        <Home path="/" />
        <Dashboard path="dashboard" />
      </Router>
    </>
  );
}

From the code block above, our Home and Dashboard components are children of the Router component that we imported from Reach Router. Our components also carry a path attribute which should be equal to the to value of the appropriate Link. Now if you click on any link, it should lead to the appropriate component.

Now let’s move on to Nested Routing

Nested Routing

Now let’s say we want to add links which lead to different routes, to our Dashboard component. These links may lead to different menus on our dashboard. With Reach Router, we can nest the components we want to route to in our Dashboard component. Let’s see how this works.

Firstly, we have to create two new components which will serve as our menus here.

const Route1 = () => {
  return (
    <div>
      <h1>This is Route 1</h1>
    </div>
  );
};
const Message = () => {
  return (
    <div>
      <h1>message</h1>
    </div>
  );
};

Now that we have our menus ready, let’s add their respective links to our Dashboard component.

const Dashboard = ({ children }) => {
  return (
    <div>
      <h1>Dashboard</h1>
      <ul>
        <li>
          <Link to="route1">Route1</Link>
        </li>
        <li>
          <Link to="message">
            Message
          </Link>
        </li>
      </ul>
      <div>{children}</div>
    </div>
  );
};

You can see that after we added the links, we added a div element which displays the current menu the user is on. Remember that this is possible because the menus will children of the Dashboard component.

Now we need to add our menus as children to the Dashboard.

function App() {
  return (
    <>
      <div style={{ display: "flex", justifyContent: "space-evenly" }}>
        <Link to="/">Home</Link>
        <Link to="dashboard">Dashboard</Link>
      </div>
      <Router>
        <Home path="/" />
        <Dashboard path="dashboard">
          <Route1 path="route1" />
          <Message path="message" />
        </Dashboard>
      </Router>
    </>
  );
}

Now when we switch to the Dashboard route, we should have two links which we can use to switch between the Route1 and Message components.

Now, what if we want to add an index component, a child of the dashboard which will render when no other child of the dashboard is being rendered, to the dashboard. We can do that by simply adding a component with path set to “/” under the Dashboard component. Our Dashboard should look like this:

<Dashboard path="dashboard">
    <DashboardIndex path = "/"/>
    <Route1 path="route1" />
    <Message path="message" />
</Dashboard>

Adding a Default (“404”) Route

In the event that the user navigates to a non-existent route, we want to show them a page that tells them that they are on a non-existent route. We have to create a route for that. This route is very similar to your regular 404 page so I like to call it a 404 route. Now with all that said, let’s write a component that returns an h1 element which says “Route not found”.

const FourOFour = () => {
  return (
    <div>
      <h1>Route not found</h1>
    </div>
  );
};

Now let’s add this 404 route as a child of the Route component. This route will not have a path attribute and it will have an attribute or prop that says default.

<Router>
    <Home path="/" />
    <Dashboard path="dashboard">
        <DashboardIndex path = "/"/>
        <Route1 path="route1" />
        <Message path="message" />
    </Dashboard>
    <FourOFour default /> 
</Router>

URL Parameters

At times, we might need to pass in props in our links which are to be used in the respective route. Let us see how we can do that with Reach Router!

Now let’s say we want to pass in a message prop to the message route when we click on the message link in our dashboard. We will have to change the value of the to attribute to this:

<Link to = "message/hello">Message</Link>

As you can see, we appended the message we want to pass,”hello”, to the value of the to attribute of the link. Now how are we going to identify that message in our Message route?

Firstly, we need to modify the path of the route.

 <Message path="message/:message" />

From the line of code above, you can see that we added “/:message” to the path. This means that a prop with the name of message will be passed to the Message route whenever we click on that link. You can now use the message props the same way you will use a regular prop.

const Message = ({message}) => {
  return (
    <div>
      <h1>{message}</h1>
    </div>
  );
};

Using destructuring syntax, we were able to get the message props from the props object of our Message component. Now when you switch to the message route, you should see hello on the screen.

Passing data from one route to another.

Now, what if we need to pass in data from the Dashboard route to the Message route? We can do that through the Link component. We just have to pass in whatever data we want to pass (usually in the form of an object) to the state attribute of the Link component. Let’s say we want to pass in an object which contains a property called fullMessage. Our Link component should look like this:

<Link to="message/hello" state={{ fullMessage: "Hey What's up" }}>
Message</Link>

In order to access this data from the Message route, we need to use the data inside props.

const Message = ({
  message,
  location: {
    state: { fullMessage }
  }
}) => {
  return (
    <div>
      <h1>{message}</h1>
      <p>{fullMessage}</p>
    </div>
  );
};

Again, using destructuring syntax we were able to access the state object which is a property of the location object which is a property of the props of our Message component. We can now use the value of fullMessage in our JSX.

Conclusion

If you made it to the end of the article, you should have a basic understanding of how Reach Router works. Reach Router can do other cool things such as animation on route change and so on. You can check out the Reach Router website for more information.


Share on social media

//