Chapter 18 Client-Side Routing

This chapter discusses how to use React to effectively develop Single Page Applications (SPA)—web applications that are located on a single web page (HTML file), but use AJAX requests and DOM manipulation to produce the appearance of multiple “web pages”. This structure is facilitated by the use of the client-side routing library react-router, which allows you to render different Components based on the browser’s URL, allowing each View (“page”) to be treated as a unique resource.

18.1 Single-Page Applications

As you’ve seen in previous chapters, the React framework lets you dynamically render different Views (Components) based on different conditions such as the state of the app. For example, you can have a blogging app that could have a blogPostId state variable, and then use that variable to determine which blog post to display. Often these Views act as entirely separate pages—you either show one View or an another. As such, you’d often like each View to be treated as an individual resource and so to have its own URI, thus allowing each View to be referenced individually. For example, each blog post could have it’s own URI, allowing a user to type in a particular URL to see a specific post (and letting that user share the post with others).

In order to achieve this effect, you can utilize client-side routing. With client-side routing, determining which View to display based on the URL (how to “route”, or map that URL to the correct resource) is performed on the client-side by JavaScript code. This is distinct from server-side routing, in that the server isn’t deciding which resource to show (i.e., which .html file to respond to a request with), but rather responds with a single HTML file whose JavaScript dynamically determines what resource to show (i.e., which React component to render) based on the URI that request was sent to!

  • In this context, “routing” involves taking the resource identifier (the URI) and determining what representation of that resource should be displayed—what View to show. A “route” is thus a URI, which will refer to a particular View of the resource.

Client-side routing allows you to have unique URLs for each View, but will also make the app work faster—instead of needing to download an entire brand new page from the server, you only need to download the requisite extra data (e.g., using an AJAX request), with much of the other content (the HTML, CSS, etc) already being in place. Moreover, this will all your app to easily share both state data and particular components (e.g., headers, navigation, etc).

  • Google Drive is a good example of a Single-Page Application. Notice how if you navigate to a new folder, the URL changes (so you can link to individual folders), but only a single “pane” of the page changes.

Because React applications are component-based, you can perform client-side routing in React by using conditional rendering to only render components if the current route is correct. This follows a structure similar to:

function App() {
    //pick a component based on the URL
    let componentToRender = null;
    if(currentUrl === 'domain/home'){ //pseudocode comparison with URL
        componentToRender = <HomePage />;
    else if(currentUrl === 'domain/about'){
        componentToRender = <AboutPage />;
    //render that component
    return componentToRender;

That is, if the current URL matches a particular route, then the Component will be rendered.

18.2 React-Router

Third-party libraries such as React Router provide Components that include this functionality, allowing you to easily develop single-page applications.

This chapter details how to use version 5 of React Router, released in March 2019. This version is significantly different from the previous versions (2.x and 3.x particularly). Be careful when looking up examples and resources that they’re utilizing the same version as you!

As with other libraries, you begin using React Router by installing the react-router-dom library (the browser-specific version of React Router):

npm install react-router-dom

You will then need to import any Components you wish to use into the .js files containing your React code. For example:

//import BrowserRouter (but call it `Router`), Route, and Link
import { BrowserRouter as Router, Route, Link} from 'react-router-dom'

These Components are described in the following sections.


The <BrowserRouter> Component (which is often imported with an alias, causing it to be instantiated as <Router>—though there is also a <Router> component!) is the “base” Component used by React Router. This Component does all the work of keeping the React app’s UI (e.g., which Components are rendered) in sync with the browser’s URL. The BrowserRouter “listens” for changes to the URL, and then passes information about the current route (called the path) to its child components as a prop. This allows each child to always know what route is currently shown in the URL, without needing to access it directly.

  • With React Router, a “route” is defined by the path portion of a URI (see Chapter 2). This is the part that comes after the protocol and domain (e.g., after the Thus the /home route would refer to the URI, while the /about route would refer to the URI

  • BrowserRouter utilizes the HTML5 history API to interact with the brower’s URL and history (what allows you to go “back” and “forward” between URLs). This API is supported by modern browsers, but older browsers (i.e., IE 9) would need to use <HashRouter> as an drop-in alternate. HashRouter uses the fragment identifier portion of the URI to track what “page” the app should be showing, causing URL’s to include an extra hash # symbol in them (e.g.,

  • Your app will only ever have a single <BrowserRouter> component in it–usually at the “top level” of your application (so it would contain <App> as a child). The below examples however show the <BrowserRouter> as a child of <App> for readability.

Inside (as a child of) the <BrowserRouter>, you can specify route-based views using the <Route> Component. This component will render its content only when the URL matches a specified path. In effect, the Route Component handles checking if the current URL matches the specified path, and if so renders its content. If the URL doesn’t match the route, then the content is not rendered. The path to match is passed in as prop:

function App() {
    return (
            {/* if currentUrl == "/home" */}
            <Route path="/home" >
                <HomePage />

            {/* if currentUrl == "/about" */}
            <Route path="/about">
                <AboutPage />

The path prop is used to indicate the route that you wish to match. This route should always start with a leading / (since it’s the path that comes after the domain in the URI). Note that this can be a multi-part path (e.g., /assignments/react), and can even include URL parameters (see below).

  • Note that by default the path will “match” even if it is contained in only part of the URL. For example, <Route path="/about" /> will match a URL of /about OR /about/me. A path of / will match any URL! You can customize what how strict the matching is by specifying the exact prop (indicating that the path has to match entirely) and/or the strict prop, which will cause the router to respect any trailing / you include.

  • Importantly, each <Route> determines whether it should render its content independently from each other: they are each if statements, not if else statements! Thus it is possible for more than one <Route> to match the current URL and render its content (and you may actually want to do this sometimes if the content is only a part of a “page”). However, it is very common to have each “route” be mutually exclusive so that only one “page” is shown at a time. You can enforce this by nesting the <Route> elements inside of a <Switch> element:

        <Route path="/home"> <HomePage /> </Route>
        <Route path="/about"> <AboutPage /> </Route>

    This can be particularly useful when working with URL Parameters.

    Pro tip: It is often useful to specify the routes as a const variable (e.g., routes) that is an object containing paths and which component to render for that path. Then you can use a map() operation to render those <Route> elements. This makes it easy to check and change the URIs used in your page later. See Route Config for an example.

    Note that you can use the useRouteMatch() hook to get access to the current path and url; this can be useful for building relative links.

URL Parameters

It is also possible to include variables in the matched route using what are called URL Parameters. As you may recall from reading a RESTful API, URI endpoints are often specified with “variables” written using :param syntax (a colon : followed by the parameter name). For example, the URI

from the Github API refers to a particular user—you can replace :username with any value you want: refers to the joelwross user, while refers to the mkfreeman user.

React Router supports a similar syntax when specifying Route paths. For example:

<Route path='/post/:postId'/>

will match a path that starts with /post/ and is followed by any other path segment (e.g., /post/hello, /post/2017-10-31, etc). The :postId (because it starts with the leading :) will be treated as a parameter which will be assigned whatever value is part of the URI in that spot—so /post/hello would have 'hello' as the postId, and /post/2017-10-31 would have '2017-10-31' as the postId.

You can access the values assigned to the URL parameters by using the useParams hook provided by react-router. This hook returns an object whose keys are the parameter names and whose values are the param values:

function BlogPost() {
    //access the URL params as an object
    //it's also common to use object destructuring here
    const urlParams = useParams();
    return (
        {/* postId was the URL parameter from the above example! */}
        <h1>You are looking at blog post {urlParams.postId}</h1>

Nesting Routes

As you’re working with React Router, remember that <Route> elements are just React Components (that include an if statement causing them not to render if the URL is incorrect). That means that—as long as they are inside a <Router>, you can include them anywhere inside your application, mixing them in with normal HTML and React Components:

function App() {
    return (
                <h1>Page Title</h1>
            <Route path="/home"> <HomePage /> </Route>
            <Route path="/about"> <AboutPage /> </Route>

function HomePage() {
    //access the route "match" props using the `useRouteMatch` hook
    const routeProps = useRouteMatch()

    return (
            <h2>Home Page</h2>
            {/* if route includes /blog, show the blog */}
            {/* Note use of a the relative url! */}
            <Route path={routeProps.url + '/blog'}>
                <BlogPartial />


While specifying <Route> elements will allow you to show different “pages” at different URLs, in order for a Single Page Application to function you need to be able to navigate between routes without causing the page to reload. Thus you can’t just use normal <a> elements to link between “pages”—browsers interpret clicking on <a> elements as a command to send a new HTTP request, and you instead just want to change the URL and re-render the App.

Instead, React Router provides a <Link> element that you can use to create a hyperlink to another route within the application. This component takes a to prop that you use to specify the route that it links to:

<Link to="/about">Click to visit the About Page</Link>
  • The component will render as an <a> element with a special onClick handler that keeps the browser from loading a new page. Thus you can specify any content that you would put in the <a> (such as the hyperlink text) as child content of the <Link>.

  • It is also possible to specify additional parts (e.g., query parameters, fragments) as part of the link. See the documentation for details.

  • React Router also provides a <NavLink> Component that lets you specify a specific CSS class or styling that should apply to the element if the to route matches the current route. This is used for example to have a navigation section “highlight” the link to the page you’re currently on, helping the user understand where they are on the page.

Finally, React Router provides a <Redirect> component that, when rendered, will navigate the browser to the given route. This will allow you to “programmatically” navigate the user to a different route (without requiring the user to click on a link)—you just need to render the <Redirect> and the page will change.

  • Note that in order for the <Redirect> to work, you need to render it (e.g., return it from a component as the DOM to render). Thus a good way to programmatically redirect is to use a prop or a state variable to determine whether a component should render its DOM content or a <Redirect> and then conditionally render the <Redirect> if that state variable becomes true:
function LoginPage() {
    //state variable to track if user is logged in, initially false
    const [loggedIn, setLoggedIn] = useState(false)

    //effect hook to check if a user is logged in
    useEffect(() => {
        //hypothetical AJAX method to check if user is logged in
        checkAuthenticationState().then((status) => {
          if(status === LOGGED_IN) //user is logged in
            setLoggedIn(true); //re-render, but redirect

    if(!loggedIn) { //not logged in, so show the login form
        return (
                <form class="login-form">
    else { //otherwise, redirect
        return <Redirect to="/userPage" />;        

(In practice you’d track whether the user is logged in or not probably have a top-level component such as <App> check for user authentication, and then pass whether the user is logged in as a prop, using that to determine whether to redirect or not)

Caution: you should not render a <Redirect> element as the child of displayed content (e.g., inside a <div>). This can cause issues with the redirect taking multiple “DOM update cycles” to process, interfering with your application’s processing. Instead, determine whether you should redirect and if so return just the <Redirect> element (e.g., with a “break early” sentinel condition).

That covers most of the basic features of React Router. Be sure to check out the documentation for more details, as well as the extensive examples (though they use some alternate, less readable React syntax).

React Router and Github Pages

React Router’s client-side routing introduce a few additional considerations when the you wish to deploy your app on a non-development server, such as Github Pages (e.g., what happens when you deploy a create-react-app project).

First, consider what happens when you type a route (e.g., to access the /about route) into the browser’s URL bar in order to navigate to it. This creates an HTTP Request for the resource at the URI with an /about path. When that request is received by the web server, that server will perform server-side routing and attempt to access the resource at that location (e.g., it will look for an /about/index.html page). But this isn’t what you want to happen&dmdash;because there is no content at that resource (no /about/index.html), the server will return a 404 error.

Instead, you want the server to take the request for the /about resource and instead return your root /index.html page, but with the appropriate JavaScript code which will allow the client-side routing to change the browser’s URL bar and show the content at the /about route. In effect, you want the server to be able to return your root index.html page no matter what route is specified in the HTTP Request!

It is perfectly possible to have a web server do this (to not perform server-side routing and instead always return /index.html no matter what resource is requested); indeed, this is what the Create React App development server does. However GitHub Pages doesn’t have this functionality: if you send an HTTP request for a resource that doesn’t exist (e.g., /about), you will receive a 404 error. There are a few ways to work around this:

  1. You can utilize a <HashRouter> instead of a <BrowserRouter> The <HashRouter> uses the fragment identifier portion of the URI to record and track which route the user is viewing: the HTTP request is thus sent to to get the /about route—and since index.html is the default resource, this can be abbreviated to, which is almost as good. In this way you are always requesting the appropriate resource (/index.html), but can still perform client-side routing. The trade-off is that your URLs will have extraneous # symbols in them (which also makes utilizing inner-page navigation with the fragment more difficult), and going to will still cause a 404 error.

  2. The other approach is to replace Github Page’s 404 page with something that goes to your index.html (using server-side routing)—so instead of the user being shown the 404, they are shown your index.html which is about to do the client-side routing! spa-github-pages provides some boilerplate for doing this, but it is a “hacky” approach as is not recommended.

  3. The best approach would be to utilize a different web hosting system that better supports the server-side routing needed for single-page applications. For example, Firebase Hosting allows you to specify a rewrite rule that will cause the server to return your index.html no matter which route the HTTP Request specifies. Create React App also has some details about deploying to Firebase.

Second, in addition to the server-side routing issue, you will need to do extra work to handle <Redirect> elements if your application’s URL is in a subresource of the server (e.g., it can be found at While <Link> elements will route correctly in this case (<Link to='/home'> will go to, <Redirect> elements will overwrite the path part of the URI: <Redirect to='/home'> will take you to to, losing the fact that your application is in /app resource.

Luckily, it is easy to support this behavior and tell React Router that all paths should be treated relative to that subresource (relative to the /app path). You do this by passing the basename prop to the <Router>:

<BrowserRouter basename={process.env.PUBLIC_URL+'/'}>
  • This example specifies that the “base” uri should be whatever URI was listed in the "homepage" key of the project’s package.json folder, such as what you specifying when deploying Create React App to Github Pages. You would want to use that exact expression (process.env.PUBLIC_URL), which is a global variable referring to the _env_ironment of the bundling node process; the PUBLIC_URL key is assigned the homepage property by Create React App.