Client Side Routing on GitHub Pages with Create React App
By: Regina Furness
Tags: React, Deployment, Create React App, Client Side Routing, Github Pages

Photo by Ferenc Almasi on Unsplash

So in my last blog post I talked about incorporating EmailJS in my portfolio website. While I had some questions, the process of doing that was actually fairly easy. On the other hand, when I deployed to GitHub Pages that’s when things got messy. The process of actually deploying was about as seamless and simple as it gets, but once I deployed I realized my client side routing was not working, and it took me a while to figure out how to fix it.

Deploying to GitHub Pages

If you already know how to deploy an application bootstrapped with Create React App to GitHub Pages, I suggest you skip to the next section about fixing your client side routing. If you don’t know, don’t worry because this is the easy part!

First Step

run either:

yarn add gh-pages

or

npm install --save gh-pages

Second Step

You need to add a homepage field in package.json . According to the Create React App Docs, Create React App uses the homepage field to determine the root URL in the built HTML file. If you didn’t a custom domain page by adding a CNAME file (which I didn’t) you’ll add something like this to the top of your package.json :

"homepage": "https://your-github-username.github.io/your-project-repo-name"

If you want to have a custom domain page URL I would suggest following this article. After that, it’s as simple as using that URL in the homepage field in your package.json .

Third Step

You will need to add predeploy and a deploy script to package.json .

"predeploy": "yarn run build",

"deploy": "gh-pages -d build",

or

"predeploy": "npm run build",

"deploy": "gh-pages -d build",

Fourth Step

Simply run either:

yarn run deploy

or

npm run deploy

Final Step

Go to your project repo on GitHub, go to settings.

Settings for Project Repo on GitHub

Scroll down to the GitHub Pages section.

What GitHub Pages fields should look like

In the green section you should see the URL that you have in your homepage field of package.json . In addition you want to make sure that the branch that is chosen is the gh-pages branch that I have highlighted. If it’s not, select that branch and then save.

Client Side Routing

Alright, so you should be deployed on GitHub Pages now. Only problem is, when you navigate to your domain URL, you may be getting a 404 error. Or perhaps the domain URL is fine but when you navigate to different routes you get a 404 error.

I missed this very important advisory when reading through the Create React App Docs on Deployment:

GitHub Pages doesn’t support routers that use the HTML5 pushState history API under the hood (for example, React Router using browserHistory ). This is because when there is a fresh page load for a url like http://user.github.io/todomvc/todos/42 , where /todos/42 is a frontend route, the GitHub Pages server returns 404 because it knows nothing of /todos/42 .

I’m going to walk you through what I did to get my client side routing working. There is another way to do it using HashRouter that I will not be going over in this blog.

Starting Out

So I used BrowserRouter , and Switch . At the top of my App.js file I had import { BrowserRouter as Router, Switch, Route } from ‘react-router-dom’;

To start off my routes looked like this:

<Router> <Switch> // These could also look like:

// <Route exact path='/' component={Home}/>

<Route exact path='/'>

<Home/>

</Route> <Route exact path='/portfolio'>

<Portfolio/>

</Route> <Route exact path='/contact' >

<Contact/>

</Route> <Route exact path='/resume'>

<Resume/>

</Route> </Switch> </Router>

First Step

So the first change I made was using PUBLIC_URL:

<Router basename={public.env.PUBLIC_URL}> <Switch> <Route exact path='/'>

<Home/>

</Route> <Route exact path='/portfolio'>

<Portfolio/>

</Route> <Route exact path='/contact' >

<Contact/>

</Route> <Route exact path='/resume'>

<Resume/>

</Route> </Switch> </Router>

So what is PUBLIC_URL ? As usual you can get a good description of it from the Create React App Docs:

Information on PUBLIC_URL from Create React App Docs

What is basename doing? From the React Router Docs:

Description of basename property of BrowserRouter from React Router Docs

Without this step, /project-repo-name would be treated as a pathname, rather than part of the base URL. So rather than navigating to https://github-username.github.io/project-repo-name/portfolio it would try to navigate to https://github-username.github.io/portfolio .

I still had one problem, though, if someone refreshed while they were on a route that wasn’t '/' it would cause a 404 error.

Second Step

So why was I still getting 404 errors on refresh? Well it goes back to the fact that GitHub Pages is hosting your homepage URL but it knows nothing of your client side routing, it doesn’t recognize those routes as being part of the same application.

The solution lies in creating a 404.html file and putting it in your public folder. This file will include a script that converts the path into a query string then redirects to a URL that’s the query string and a hash fragment. GitHub Pages will ignore the query string and just return the index.html file. In index.html you’ll include a script that will check for a query string and then convert it to a URL that will be added to the browser’s history using window.history.replaceState() , once the Single Page Application script (this loads in index.html ) is finished loading it will redirect to the correct URL and therefor the correct page.

If that sounds like a lot, don’t worry. The code for all of this is available here. All credit goes to Rafael Pedicini and anyone else who may have contributed to the repository. You will just need to take the code from the 404.html file in that repository and put it in your own 404.html file (remember to have this in the public folder). If you didn’t use a custom domain page URL you may need to change the value of pathSegmentsToKeep to 1. Like so:

// this will start out equal to 0

var pathSegmentsToKeep = 1; var l = window.location; l.replace(l.protocol + '//' + l.hostname + (l.port ? ':' + l.port : '') + l.pathname.split('/').slice(0, 1 + pathSegmentsToKeep).join('/') + '/?/' + l.pathname.slice(1).split('/').slice(pathSegmentsToKeep).join('/').replace(/&/g, '~and~') + (l.search ? '&' + l.search.slice(1).replace(/&/g, '~and~') : '') + l.hash

);

NOTE: Take the full 404.html file from the GitHub repo, not only is this not everything you need, but it lacks the proper attribution to the creator of this code.

Then you will need to copy the redirect script into your index.html file. For me I had it inside <head> underneath everything else. The documentation says the script has to be before the Single Page Application script, so if you have issues for some reason, I would try to move it up further in the <head> .

Final Steps

Now all you should have to do is commit and push your changes to GitHub and then run either:

yarn run deploy

or

npm run deploy

Your client side routing should be working, and even if you refresh on any of the paths, they should load back up!

In Conclusion