Identity management for Zeo

Single Page Apps for GitHub Pages

Live example

This is a lightweight solution for deploying single page apps with GitHub Pages. You can easily deploy a Reactsingle page app with React Router<BrowserRouter />, like the one in the live example, or a single page app built with any frontend library or framework.

Why it's necessary

GitHub Pages doesn't natively support single page apps. When there is a fresh page load for a url like example.tld/foo, where /foois a frontend route, the GitHub Pages server returns 404 because it knows nothing of /foo.

How it works

When the GitHub Pages server gets a request for a path defined with frontend routes, e.g. example.tld/foo, it returns a custom 404.htmlpage. The custom 404.htmlpage contains a scriptthat 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. For example, example.tld/one/two?a=b&c=d#qwe, becomes example.tld/?p=/one/two&q=a=b~and~c=d#qwe.

The GitHub Pages server receives the new request, e.g. example.tld?p=/..., ignores the query string and hash fragment and returns the index.htmlfile, which has a script that checks for a redirect in the query stringbefore the single page app is loaded. If a redirect is present it is converted back into the correct url and added to the browser's history with window.history.replaceState(...), but the browser won't attempt to load the new url. When the single page app is loadedfurther down in the index.htmlfile, the correct url will be waiting in the browser's history for the single page app to route accordingly. (Note that these redirects are only needed with fresh page loads, and not when navigating within the single page app once it's loaded).

A quick SEO note - while it's never good to have a 404 response, it appears based on Search Engine Land's testingthat Google's crawler will treat the JavaScript window.locationredirect in the 404.htmlfile the same as a 301 redirect for its indexing. From my testing I can confirm that Google will index all pages without issue, the only caveat is that the redirect query is what Google indexes as the url. For example, the url example.tld/aboutwill get indexed as example.tld/?p=/about. When the user clicks on the search result, the url will change back to example.tld/aboutonce the site loads.

Usage instructions

For general information on using GitHub Pages please see GitHub Pages Basics, note that pages can be User, Organization or Project Pages

Basic instructions- there are two things you need from this repo for your single page app to run on GitHub Pages

  1. Copy over the 404.htmlfile to your repo as is
    • Note that if you are setting up a Project Pages site and not using a custom domain(i.e. your site's address is username.github.io/repo-name), then you need to set segmentCountto 1in the 404.htmlfilein order to keep /repo-namein the path after the redirect.
  2. Copy the redirect scriptin the index.htmlfile and add it to your index.htmlfile
    • Note that the redirect script must be placed beforeyour single page app script in your index.htmlfile  

Detailed instructions- using this repo as a boilerplate for a React single page app hosted with GitHub Pages

  1. Clone this repo ($ git clone https://github.com/rafrex/spa-github-pages.git)
  2. Delete the .gitdirectory (cdinto the spa-github-pagesdirectory and run $ rm -rf .git)
  3. Instantiate the repository
    • If you're using this boilerplate as a new repository
      • $ git initin the spa-github-pagesdirectory, and then $ git add .and $ git commit -m "Add SPA for GitHub Pages boilerplate"to initialize a fresh repository
      • If this will be a Project Pages site, then change the branch name from masterto gh-pages($ git branch -m gh-pages), if this will be a User or Organization Pages site, then leave the branch name as master
      • Create an empty repo on GitHub.com (don't add a readme, gitignore or license), and add it as a remote to the local repo ($ git remote add origin <your-new-github-repo-url>)
      • Feel free to rename the local spa-github-pagesdirectory to anything you want (e.g. your-project-name)
    • If you're adding this boilerplate as the gh-pagesbranch of an existing repository
      • Create and checkout a new orphaned branch named gh-pagesfor your existing repo ($ git checkout --orphan gh-pages), note that the gh-pagesbranch won't appear in the list of branches until you make your first commit
      • Delete all of the files and directories (except the .gitdirectory) from the directory of your existing repo ($ git rm -rf .)
      • Copy all of the files and directories (including hidden dot files) from the cloned spa-github-pagesdirectory into your project's now empty directory ($ mv path/to/spa-github-pages/{.[!.],}* path/to/your-projects-directory)
      • $ git add .and $ git commit -m "Add SPA for GitHub Pages boilerplate"to instantiate the gh-pagesbranch
  4. Set up a custom domain (optional) - see GitHub Pages instructions for setting up a custom domain
    • Update the CNAMEfilewith your custom domain, don't include http://, but do include a subdomain if desired, e.g. wwwor your-subdomain
    • Update your CNAMEand/or Arecord with your DNS provider
    • Run $ dig your-subdomain.your-domain.tldto make sure it's set up properly with your DNS (don't include http://)
  5. Set up without using a custom domain (optional)
    • Delete the CNAMEfile
    • If you are creating a User or Organization Pages site, then that's all you need to do
    • If you are creating a Project Pages site, (i.e. your site's address is username.github.io/repo-name):
      • Set segmentCountto 1in the 404.htmlfilein order to keep /repo-namein the path after the redirect
      • Add your repo-nameto the absolute path of assets in index.html
      • If you are using React Router you'll need to tell it to use the repo-nameas the basename, for example <BrowserRouter basename="/repo-name" />
  6. Run $ npm installto install React and other dependencies, and then run $ npm run buildto update the build
  7. $ git add .and $ git commit -m "Update boilerplate for use with my domain"and then push to GitHub ($ git push origin gh-pagesfor Project Pages or $ git push origin masterfor User or Organization Pages) - the example site should now be live on your domain
  8. Create your own site
    • Write your own React components, create your own routes, and add your own style
      • Note that the example site is created with all inline styles and uses React Interactivefor the links and other interactive components (there is no CSS except for a reset in index.html)
    • Change the title in index.htmland the title in 404.htmlto your site's title
    • Remove the favicon linksfrom the header of index.html
    • Change the readme, license and package.json as you see fit
    • For testing changes locally see development environment info below
    • To publish your changes to GitHub Pages run $ npm run build(this runs webpack -pfor production) to update the build, then $ git commitand $ git pushto make your changes live

Development environment

I have included webpack-dev-serverfor testing changes locally. It can be accessed by running $ npm start(details below), or you can use your own dev setup by running $ webpackand serving the index.htmlfile and the 404.htmlfile for 404s. Note that webpack-dev-serverautomatically creates a new bundle whenever the source files change and serves the bundle from memory, so you'll never see the bundle as a file saved to disk.

  • $ npm startruns the start scriptin package.json, which runs the command $ webpack-dev-server --devtool eval-source-map --history-api-fallback --open
    • -devtool eval-source-mapis for generating source mapsin while in development
    • --history-api-fallbackallows for frontend routing and will serve index.htmlwhen the requested file can't be found
    • --openwill open automatically open the site in your browser
  • webpack-dev-serverwill serve index.htmlat http://localhost:8080(port 8080is the default). Note that you must load the index.htmlfrom the server and not just open it directly in the browser or the scripts won't load.


  • The .nojekyllfile in this repo turns off Jekyll for GitHub Pages
  • Need form submission on your static site? Use Formspree
  • One of the great things about the GitHub Pages CDN is that all files are automatically compressed with gzip, so no need to worry about compressing your JavaScript, HTML or CSS files for production







扫码加入 JavaScript 社区