Author avatar

Vivek Kumar

Deploying CRA to GitHub Pages Using React Router

Vivek Kumar

  • Feb 1, 2020
  • 9 Min read
  • 98 Views
  • Feb 1, 2020
  • 9 Min read
  • 98 Views
Web Development
React

Introduction

In today’s work culture, it's important not only to create effective products, but also to minimize the time and effort invested in their development. That's why professionals in the field of app development always emphasize ways of creating apps that can save time and effort for clients in the long run. For instance, if you're creating React apps, you can find numerous tools and techniques that help accomplish the development process in no time.

Create React App (CRA) is one such tool that gives you a huge head start. It can save you a good amount of time by sidestepping the complex setup and configuration steps you normally encounter while following the standard procedure. All you have to do is run a single command and CRA sets up the tools for you to start your React project.

The deployment of CRA to GitHub Pages is important to fasten the data backup and retrieval process so you can create the finest React apps possible within a stipulated time frame. In this guide, we will focus on effective deployment, including how to accomplish it with the help of React Router.

SPA Deployment: A Lightweight Solution

Please note that you can easily deploy a React single-page app (SPA) with the help of React Router <BrowserRouter />, including any SPA developed with a frontend framework or library.

It's important to note that GitHub Pages does not support SPA natively. If there is fresh page load of a URL such as example.tld/foo, in which /foo is a frontend route, it is obvious that the GitHub Pages server will return a 404 message because it doesn’t have any details of /foo.

How It Works

When the GitHub Pages server receives a request meant for a path defined by frontend routes—for instance, example.tld/foo—it returns a standard 404.html page. That page contains a script for the current URL and thus changes the path into a query string. Thereafter, it redirects the browser to a new URL comprising a query string along with a hash fragment.

For example, ghpages.tld/bar/baz?a=b&c=d#qwe turns into ghpages.tld/?p=/bar/baz&q=a=b~and~c=d#qwe.

The GitHub Pages server receives a new request in the form of ghpages.tld?p=/. It overlooks the query string and the hash fragment and then returns the index.html file. This file is comprised of a script that verifies the redirect in the query string. This step takes place prior to the loading of the SPA.

If a redirect is present in the link, it is simply changed back to the correct URL and included in the browser history using window.history.replaceState(…), but there is not an approval from the browser’s end to load the new URL.

Now, when the SPA is loaded further down in the index.html file, then the correct URL is ready in the browser history to route. It is critical to note that such redirects are only required with the loading of a fresh page. They do not play any role in navigating within the SPA once it is loaded .

Instructions

You need two things in order to run your SPA on GitHub Pages.

First, copy the HTML content provided below to your 404.html file.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Single Page Apps for GitHub Pages</title>
    <script type="text/javascript">
      // Single Page Apps for GitHub Pages
      // https://github.com/rafrex/spa-github-pages
      // Copyright (c) 2016 Rafael Pedicini, licensed under the MIT License
      // ----------------------------------------------------------------------
      // This script takes the current url and converts the path and query
      // string into just a query string, and then redirects the browser
      // to the new url with only a query string and hash fragment,
      // e.g. http://www.foo.tld/one/two?a=b&c=d#qwe, becomes
      // http://www.foo.tld/?p=/one/two&q=a=b~and~c=d#qwe
      // Note: this 404.html file must be at least 512 bytes for it to work
      // with Internet Explorer (it is currently > 512 bytes)

      // If you're creating a Project Pages site and NOT using a custom domain,
      // then set segmentCount to 1 (enterprise users may need to set it to > 1).
      // This way the code will only replace the route part of the path, and not
      // the real directory in which the app resides, for example:
      // https://username.github.io/repo-name/one/two?a=b&c=d#qwe becomes
      // https://username.github.io/repo-name/?p=/one/two&q=a=b~and~c=d#qwe
      // Otherwise, leave segmentCount as 0.
      var segmentCount = 0;

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

    </script>
  </head>
  <body>
  </body>
</html>
html

Second, copy the redirect script given below and include it in your index.html file.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
    <script type="text/javascript">
      // Single Page Apps for GitHub Pages
      // https://github.com/rafrex/spa-github-pages
      // Copyright (c) 2016 Rafael Pedicini, licensed under the MIT License
      // ----------------------------------------------------------------------
      // This script checks to see if a redirect is present in the query string
      // and 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.
      (function(l) {
        if (l.search) {
          var q = {};
          l.search.slice(1).split('&').forEach(function(v) {
            var a = v.split('=');
            q[a[0]] = a.slice(1).join('=').replace(/~and~/g, '&');
          });
          if (q.p !== undefined) {
            window.history.replaceState(null, null,
              l.pathname.slice(0, -1) + (q.p || '') +
              (q.q ? ('?' + q.q) : '') +
              l.hash
            );
          }
        }
      }(window.location))
    </script>
html

A Case Study

Say you are creating an SPA using React and React Router and pushing it to GitHub Pages. Assume that none of the routes work when you refresh or click back in your browser. In short, you are unable to go straight to pages like /help or /about as listed in the file below:

1
2
3
4
5
6
7
app.get('/help', function(req, res){
  res.sendFile('public/index.html', { root: __dirname });
})

app.get('/about', function(req, res){
  res.sendFile('public/index.html', { root: __dirname });
})
jsx

To complete the example, here is the code that includes the complete routing logic:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import React, {Component} from 'react';
import {render} from 'react-dom';
import {Router, Route, browserHistory, IndexRoute} from 'react-router';
//Import custom components
import About from '../components/about.js';
import Help from '../components/help.js';

  render(
      <Router history={browserHistory}>
          <Route path="/about" component={About}/>
          <Route path="/help" component={Contact}/>
        </Route>
      </Router>,
      document.getElementById('content')
    );
jsx

The solution to the above problem is replacing browserHistory with hashHistory.

An alternative solution, as discussed above, is to use BrowserRouter, which requires that you pass a basename prop to the component with this env variable: process.env.PUBLIC_URL.

Your router should now look like this:

1
2
3
<BrowserRouter basename={process.env.PUBLIC_URL}>
  {/* routes */}
</BrowserRouter>
jsx

There are a few alternative solutions available. You can explore them on this Stack Overflow page.

Conclusion

In this guide, we learned about deploying CRA to GitHub using React Router. We learned about SPA deployment and how it actually works.

1