Using React Router on GitHub Pages
By: Chris Liendo
Tags: Redirect, JavaScript, Github, React, 404

Now on to adding router to your React app! The first thing you’ll want to do is make sure you have the router packages installed. Run npm install react-router-dom react-router . Although we won’t be importing from react-router we’re just including it for good measure. npm install is built to take multiple arguments so you can install both in one line.

Step 1: <BrowserRouter>

In either index.js or App.js — whichever has less clutter — import BrowserRouter as Router and wrap your main component with it:

import {BrowserRouter as Router} from 'react-router-dom'

Import BrowserRouter from react-router-dom

Wrap your main <App /> component with <Router>

NOTE: in order for your app to work once deployed to GitHub Pages, the <Router> wrapping it will need to know its root URL. You must include:

basemname={`/${process.env.PUBLIC_URL}`} as a prop to <Router> .

Step 2: <Route> & <Switch>

Now let’s create some routes! A parent component that renders multiple other components is a prime candidate to start work in. First import <Route> and <Switch> from react-router-dom .

Import Switch and Route from ‘react-router-dom’

My component below was already differentially rendering my resume , about , projects , and experience pages. Now in place of each one, I’m rendering a <Route> and passing the name of the appropriate component to its component prop. The path prop allows you to define the route that will show up in the URL. For example on line 14 path='/resume' means /resume will appear in the URL.

If you are already passing down props to your components, look at like 16, below. <Route> has a render prop that allows you to define an anonymous function that returns your desired component with its own props:

render={ () => <Projects projectList={projectList} />}

A route for every ‘page’ you want to create

You can wrap all your routes within <Switch> for exclusivity. Router will match the first path if there are two of the same.

I wraped these <Route> components within a <Switch> component and also included a default ‘root’ route as well as a fallback Route that renders a fallback NotFound component.

NOTE: if you plan on deploying to GitHub Pages, the path of your ‘root’ route (here on line 33) must be '' . If set to '/' it will work locally but not once deployed.

Switch includes a root route and a ‘NotFound’ route

Step 3: <Link>

We also need to create links that point to each Route’s path . Start by importing Link from react-router-dom .

Import Link from ‘react-router-dom’

We use Link 's to prop to match each Route’s path . This is typically done in a component that handles navigation like a menu or navbar.

Links that match our Router paths

Above, I’m dynamically creating links by mapping over props.pages . Each <MenuDrawerOption> is wrapped by a <Link> . Pay attention to the to={} prop. Here is where you’ll match what you put in each Route 's path prop. In this example, I’m dynamically creating routes by resolving page to a string corresponding to a Route’s path. Had I created each Route manually, I would have had a Link with a to prop for each of my Route 's paths in Step 2. For example:

to='/resume’ → path='/resume'

to='/about’ → path='/about'

to='/projects' → path='/projects'

to='/experience' → path='/experience'

Step 4: Direct URL Navigation and Hard Refresh

At this point, we’re in good shape and should be able to navigate to different Routes by clicking around. Typing a route into the URL bar, however, will just go to the ‘root’ route or if deployed will cause a 404 error status code. My solution was to use Router’s withRouter to grant my component access to the browser’s history object. First import withRouter :

Import ‘withRouter’ from ‘react-router-dom’

Next, you’ll need to export the component that you plan on using with it as below:

Export component with ‘withRouter’

Now our component has access to the browser’s history , which includes the current location and the action taken to get there.

location is a Route path like '/home'

action is either POP or PUSH depending on if a user typed the path directly into the URL bar or clicked their way to it:

typed → POP

clicked → PUSH

You can access action and location through props.history.action and props.history.location respectively. Now that we know how the user got to the current page we can check if the action was — POP (the user typed it in) and if so, use location to make sure the active page is set.

Below, I check for POP on line 23 and use history.location.pathname to set the active page even when the user typed their way there.

Make sure the proper page is rendered by checking for ‘POP’

NOTE: this will work locally but if deploying to GitHub Pages, your <BrowserRouter> will not have access to history and will not be able to check for POP or PUSH . Even worse since GitHub Pages serves your code as a static page, it will always serve index.js and won’t know what component route should be shown. should. At this point, I hit a wall for a while but eventually found a script written by rafgraph that redirects you to the appropriate component after loading index.js.

This script checks to see if a redirect is present in the query string, converts it back into the correct url and adds it to the browser’s history using window.history.replaceState(…), which won’t cause the browser to attempt to load the new url. When the single page app is loaded further down in this file, the correct url will be waiting in the browser’s history for the single page app to route accordingly.

You can find a detailed explanation with instructions on how to include it in your project on the repository’s page!