Battle of the Routers: Reach Router vs React Router

William Le

In this article, get an overview of Reach Router along with code snippets from React Router to see how both libraries stack up against each other.

Reach Router is authored by Ryan Florence. If you recognize his name it’s because he’s the original co-author of React Router, which currently the most popular routing library with 33.5K stars on GitHub. Far as I can tell, both libraries have identical purposes to provide a routing layer for the web… But what is definitely obvious is that Reach Router is taking a fresh approach to the solution, and it also comes with accessibility (a11y) baked-in and working out of the box.

The Basic Setup

Reach Router is very similar to React Router and uses similar namespaces like <Router>, <Link>, etc., but there’s some noteworthy differences. Let’s look at basic setups for both reach-router and react-router.

First, a setup using Reach Router:

//////////////////
//Reach-Router////
//////////////////

import React from "react";
import { Router } from "@reach/router";

const App = () => {
  return (
    <div>
      <h1>Global Party Supplies, Inc</h1>
      <Router>                           //  PATHS
        <Home path="/"/>                 //  "/"
        <About path="about"/>            //  "/about"
        <Support path="support"/>        //  "/support"
        <Dashboard path="dashboard">     //  "/dashboard"
          <Report path=":reportId"/>     //  "/dashboard/:reportId"
          <Invoices path="invoices"/>    //  "/dashboard/invoices"
          <Team path="team"/>            //  "/dashboard/team"
        </Dashboard>
      </Router>
    </div>
  );
}

My first impression is that this looks really clean. Using nested JSX for subpaths (/dashboard/team) distinguishes itself from React Router. But wait… there’s something weird here. At first glance you may think the <Report> route would intercept /dashboard/invoices and /dashboard/team, but it actually doesn’t! Reach Router uses a point system to rank your route definitions by looking at the path value. When two paths seem to collide, params (:reportId) and wildcard (*) get low-priority, while explicit paths (invoices and teams) get higher priority. This means navigating to /dashboard/invoices actually works, and /dashboard/foo goes to <Report>.


Let’s now look at React Router:

//////////////////
//React-Router////
//////////////////

import React from "react";
import { BrowserRouter as Router, Route } from "react-router-dom";

const App = () => {
  return (
    <Router>
      <div>
        <h1>Global Party Supplies, Inc</h1>                          //  PATHS
        <Route component={Home}       path="/" exact/>               //  "/"
        <Route component={About}      path="/about"/>                //  "/about"
        <Route component={Support}    path="/support"/>              //  "/support"
        <Route component={Dashboard}  path="/dashboard"/>            //  "/dashboard"
        <Route component={Invoices}   path="/dashboard/invoices"/>   //  "/dashboard/invoices"
        <Route component={Team}       path="/dashboard/team"/>       //  "/dashboard/team"
        <Route component={Report}     path="/dashboard/:reportId"/>  //  "/dashboard/:reportId"
      </div>
    </Router>
  );
}

React Router has its own <Route> component that takes two props: component and path. Whereas Reach Router doesn’t have a <Route> component… You just use the component itself (eg., <Dashboard>)!

Params & Props

Now that we’ve seen the basic setup, let’s see how you’d pass data to routes.

Using Reach Router:

//////////////////
//Reach-Router////
//////////////////

// Route definition
<Report
  path="dashboard/:reportId"  // static routes work too :)
  salesData={this.state.salesData}
>

const Report = (props) => {
  return (
    <h1>
      {props.reportId}
      and
      {props.salesData}
    </h1>
  )
}

That’s it?? It’s almost too concise… And now with React Router:

//////////////////
//React-Router////
//////////////////

// Route definition
<Route
  path="/dashboard/:reportId"
  render={(props) => {
    return <Report {...props} salesData={true} />
  }}
/>

const Report = ({ props, match }) => {
  return (
    <h1>
      {match.params.reportId}  // "match" is from react-router
      And
      {props.salesData}
    </h1>
  )
}

Ah, yes… Now I remember how it looked like. There’s more stuff involved with React Router, but this is normal fare if you’ve been using it for any amount of time. Let’s take a look at how <Link>ing works in both libraries.

Linking

Both Reach Router and React Router export a <Link> component, but there’s some differences. Let’s take a look at our <Dashboard> component, which has several subpages:

//////////////////
//Reach-Router////
//////////////////

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

const Dashboard = () => {
  return (
    <div>
      <div>
        <Link to="invoices">Invoices</Link>
        <Link to="team">Team</Link>
      </div>

      <!-- content -->

      <div>
        <Link to="/">Home</Link>
        <Link to="/about">About</Link>
        <Link to="/support">Support</Link>
        <Link to="../">Go Back</Link>
      </div>
    </div>
  )
}

Whoa! Relative links! It also fully sports Unix directory navigation like <a> tags would.

And now with React Router:

//////////////////
//React-Router////
//////////////////

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

const Dashboard = ({ match, history }) => {
  return (
    <div>
      <div>
        <Link to={`${match.url}/invoices`}>Invoices</Link>
        <Link to={`${match.url}/team`}>Team</Link>
      </div>

      <!-- content -->

      <div>
        <Link to="/">Home</Link>
        <Link to="/about">About</Link>
        <Link to="/support">Support</Link>
        <a onClick={history.goBack}>Go Back</a>
      </div>
    </div>
  );
}

With React Router you have to be more specific with <Link> which usually means you’ll have to use match.url when you have subpaths beyond a root subpath (eg., /host/somethingFoo/👉somethingBar👈). All of this is intelligently handled by Reach Router under the hood. Also, note that you have to programmatically “go back” since there isn’t a utility for relative navigation.

Conclusion

Based on these comparisons, Reach Router is a very tempting alternative to React Router. Overall, it’s more elegant as regards to form (the way it looks), and function (you write less, and actually do more!). This shouldn’t come as a surprise as Ryan Florence is the co-author of react-router, and most hard-fought insights can only be gotten through time & experience. It seems to show with reach-router.

Try giving Reach Router a try! The first v1.0 release was one year ago (May 2018), and GatsbyJS v2 has already opted for Reach Router against React Router (announcement).

Learn More

  • Video Tour: Ryan Florence does a video tour of Reach Router
  • Official Docs: The docs are high-quality, and include lots of interactive CodeSandbox demos
  Tweet It

🕵 Search Results

🔎 Searching...

Sponsored by #native_company# — Learn More
#native_title# #native_desc#
#native_cta#