Git Product home page Git Product logo

spa-github-pages's Introduction

Single Page Apps for GitHub Pages

Demo app

This is a lightweight solution for deploying single page apps with GitHub Pages. You can easily deploy a React single page app with React Router <BrowserRouter />, like the one in the demo app, 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 /foo is 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.html page. The custom 404.html page contains a script that 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/?/one/two&a=b~and~c=d#qwe.

The GitHub Pages server receives the new request, e.g. example.tld/?/..., ignores the query string and returns the index.html file, which has a script that checks for a redirect in the query string before 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 loaded further down in the index.html file, 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).

Usage instructions

For general information on using GitHub Pages please see Getting Started with GitHub Pages, 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.html file 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 pathSegmentsToKeep to 1 in the 404.html file in order to keep /repo-name in the path after the redirect. If you are using React Router you'll need to tell it to use the repo-name as the basename, for example <BrowserRouter basename="/repo-name" />.
  2. Copy the redirect script in the index.html file and add it to your index.html file - Note that the redirect script must be placed before your single page app script in your index.html file. ย 

Detailed instructions - using this repo as a boilerplate for a React single page app hosted with GitHub Pages. Note that this boilerplate is written in TypeScript but is setup to accept JavaScript files as well. It was previously written in JS and if you prefer a JS only boilerplate you can use version 6.

  1. Clone this repo ($ git clone https://github.com/rafgraph/spa-github-pages.git)
  2. Delete the .git directory (cd into the spa-github-pages directory and run $ rm -rf .git)
  3. Instantiate the repository
    • If you're using this boilerplate as a new repository
      • $ git init in the spa-github-pages directory, 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 main to gh-pages ($ git branch -m gh-pages), if this will be a User or Organization Pages site, then leave the branch name as main
      • 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-pages directory to anything you want (e.g. your-project-name)
    • If you're adding this boilerplate as the gh-pages branch of an existing repository
      • Create and checkout a new orphaned branch named gh-pages for your existing repo ($ git checkout --orphan gh-pages), note that the gh-pages branch won't appear in the list of branches until you make your first commit
      • Delete all of the files and directories (except the .git directory) 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-pages directory 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-pages branch
  4. Set up a custom domain (optional) - see GitHub Pages instructions for setting up a custom domain
    • Update the CNAME file with your custom domain, don't include https://, but do include a subdomain if desired, e.g. www or your-subdomain
    • Update your CNAME and/or A record with your DNS provider
    • Run $ dig your-subdomain.your-domain.tld to make sure it's set up properly with your DNS (don't include https://)
  5. Set up without using a custom domain (optional)
    • Delete the CNAME file
    • 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 pathSegmentsToKeep to 1 in the 404.html file in order to keep /repo-name in the path after the redirect
      • Add your repo-name to the absolute path of assets in index.html, change the bundle.js src to "/repo-name/build/bundle.js"
      • In React Router set the basename to /repo-name here like <BrowserRouter basename="/repo-name" />
      • In the start script in package.json replace --open with --open-page repo-name
      • In webpack.config.js:
  6. Run $ npm install to install React and other dependencies, and then run $ npm run build to 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-pages for Project Pages or $ git push origin main for 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 styled with Stitches and uses React Interactive for the links and other interactive components.
    • Change the title in index.html and the title in 404.html to your site's title
    • Remove the favicon links from the header of index.html and the favicon directory.
    • Update or delete robots.txt and sitemap.txt as you see fit (see SEO section below for more info)
    • 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 -p for production) to update the build, then $ git commit and $ git push to make your changes live

Serving from the /docs folder on the main branch - alternatively you can serve your site from the /docs folder instead of the root folder while your source code remains in the root folder.

  1. After following the previous set of instructions for using this repo as a boilerplate, create a /docs folder in the root and move index.html, 404.html and the /build folder into /docs
  2. Add --content-base docs/ to the start script in package.json
  3. In webpack.config.js change the output path to path: `${__dirname}/docs/build`,
  4. On GitHub in your repo settings select the /docs folder as the source for GitHub Pages

Development environment

I have included webpack-dev-server for testing changes locally. It can be accessed by running $ npm start (details below). Note that webpack-dev-server automatically 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 start runs the start script in package.json, which runs the command $ webpack-dev-server --host 0.0.0.0 --disable-host-check --open
    • --host 0.0.0.0 --disable-host-check is so you can access the site on your local network from other devices at http://[YOUR COMPUTER'S IP ADDRESS]:8080
    • --open will open automatically open the site in your browser
  • webpack-dev-server will serve index.html at http://localhost:8080 (port 8080 is the default). Note that you must load the index.html from the server and not just open it directly in the browser or the scripts won't load.

SEO

When I first created this solution in 2016 Google treated the redirect in 404.html the same as a 301 redirect and indexed pages without issue. Around 2019 Google changed their algorithm and no longer follows redirects in 404.html. In order to have all the pages on your site indexed by Google you need to create a robots.txt and sitemap.txt file to let Google know what pages exist. The robots.txt file needs to contain the location of the sitemap, and the sitemap.txt file needs to contain the redirect links for each page of your site so the crawler doesn't get a 404 response when it requests the page. To make this easier I created a sitemap link generator that transforms normal links into redirect links to use in the sitemap. I have done this for the demo site (this repo) and you can see the pages indexed here. Note that since Google is no longer associating the redirect links with the real paths, incoming links from other sites won't help your site's page rank. If you are creating a site where page rank on generic search terms is important, then I'd suggest looking for another solution. Some options are using GitHub Pages with a static site generator like Gatsby which generates an html file for each page as part of its build process, or hosting your single page app on a service that has native support for spas, like Netlify.

Miscellaneous

  • The .nojekyll file in this repo turns off Jekyll for GitHub Pages
  • 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

spa-github-pages's People

Contributors

mertcanyucel avatar rafgraph avatar tdh8316 avatar yannbertrand avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

spa-github-pages's Issues

about.html

It looks like GitHub now reserves the "about" path for itself, so it's necessary to copy 404.html to also be about.html.

Thanks for this example repository; it was incredibly helpful.

Consider add GitHub Actions Support?

Thanks for your work, that is a nice project. ๐Ÿ‘
Maybe we can consider add Github Actions support to simplify the process of deploying SPA pages?

404's

I followed the instructions for not using a custom domain and am receiving 404's. What can I do?

Routes not updating view

Hey! First off, this tip is really cool !

Now here's my setup;

I think it pretty much checks off all todos to make this work, but for some reason my Links aren't rerendering any component on click. It might be related to react-router; my understanding is that browserHistory and user.github.io/repo URLs are not playing nicely for some reason.
Since you already know about this use case, can guide me what might be wrong ?

Warning: [react-router] Location "/repo-name/" did not match any routes

Thanks a lot for this project!
now if I could only get it to work ;)

I have a react SPA hosted on gh pages, and I have two repos for it:

  1. "original": Used for dev (repo) and hosting the production app on gh-pages
  2. "staging": used only for hosting the staging app on gh-pages branch

I've bundled and deployed this commit from repo 1 to staging repo gh-branch.

tho when I go to https://goldylucks.github.io/memory-n-back-staging/ I get in the console Warning: [react-router] Location "/memory-n-back-staging/" did not match any routes

I've copied the [404.html](https://github.com/goldylucks/memory-n-yoru sback-staging/blob/gh-pages/404.html) like u said, and tried setting pathPrefix to both false/true

Also I copy pasted the script to the index.js (before the call to the spa bundle)

If it matters for any reason, I also have react-native and a nodejs a server code on that repo, tho to gh-pages only the react app and relevant assets are deployed

That's all the information I can think of that is relevant

any ideas what I'm missing?

Thanks again for this project and sry if I'm missing something really dumb ๐Ÿ’ฉ

Implement a development environment

It's quite boring to push each time we want to try a new feature.

It is surely easy to implement a simple HTTP server to allow users to develop their website on a local environment. Hot reload would also be really interesting.

I will probably do a PR to show you what I mean by that.

What do you think of the idea?

Possible to have dynamic social media cards?

Hello, is it possible to have a dynamic social media cards when this solution is used?

I've tried using react-helmet to have custom og properties on my app but doesn't seem to work as it will only see the 404.html properties. Is there a workaround to this?

Shows a blank page on a route with a path of 2 hashes or more

Everything works well with any route that has one path username.github.io/repo/:any but when It comes to a route with a path of more than one slash like /users/account, /admin/profile or /:any/:any/:any, the app shows a blank page. You can try username.github.io/repo/:any/:any/:any to reproduce the error on many react apps deployed on gh-pages. The screen is blank and the console shows a 404 error.

Static site generation for SEO

In case you want your site to be SEO-friendly, the site might benefit from being generated into a static site. ๐Ÿค”

I found https://github.com/phenomic/phenomic to be an interesting candidate, but it will take me some time to figure out the nuts and bolts of integration with spa-github-pages.

Suggestion - Avoid 404's using meta tag

Smashing Magazine wanted to solve the same problem as this repository and I think their solution is fairly elegant as well. It involves a small 404 page.

<script>
  sessionStorage.redirect = location.href;
</script>
<meta http-equiv="refresh" content="0;URL='/REPO_NAME_HERE'">

Because there's the "refresh" header, this gets translated into a 301 response within the browser (ignoring the 404 status code from the server). To use this, the SPA needs a bit of JavaScript at the very beginning to load the route from session storage and use replaceState to update the URL.

<script>
  (function(){
    var redirect = sessionStorage.redirect;
    delete sessionStorage.redirect;
    if (redirect && redirect != location.href) {
      history.replaceState(null, null, redirect);
    }
  })();
</script>

According to my quick searching, Google doesn't penalize 301 redirects nor the refresh header redirects, so this might be a viable alternative without putting the path after a hash.

Can't Find bundle.js

Hey There,
I tried your solution but for some reason the index.html can't find the bundle.js.
not sure if it's a path thing.
I'm using the default domain of my repo
my src path directs to "/repo-name/build/bundle"
also copied the script and the 404 page and change the segmentCount to 1.

Locally everything works fine but the bundle served differently obviously..
any idea what might be missing ?

Google fail to index

seems google changed how it index javascript, you can search it using "site:spa-github-pages.raflex.com", it only index home page, in the past it return many result (home dir and its some child directory)

i dont know why, but docute still works, try "site:docute.org" without quote.

Assets 404 not found with Vite + React Router

Issue
Using Vite with React Router is loading the assets folder from the deep routes

Steps to Reproduce:

  1. Navigate to frontend "/example/deep/again?something=123
  2. Refresh the page

Expected:
Navigates successfully to https://.github.io//example/deep/again
without any error

Actual:
Navigated successfully to https://.github.io//example/deep/again
as blank page WITH errors (This is assumed to be the blank 404.html that was loaded)

Error in browser console:
https://.github.io//example/deep/assets/index-[hash].css (404 NOT FOUND)
https://.github.io//example/deep/assets/index-[hash].js (404 NOT FOUND)

React-router params

Hello!
I keep id in router params.
And when the page is reloaded, a request is made to the server for this id

But for some reason, nothing happens to me instead and it seems that the whole application crashes (white screen, 404 errors in the console)

I don't understand what's wrong. Didn't find a solution..

Github pages served from master branch /docs without custom domain

Changes to run this as a Project Page served from the /docs folder on the master branch:
eg. http://username.github.io/spa

  • Create "spa" repository on your GitHub account. Rename all "spa" occurrences below if you call it something else.
  • Clone [email protected]:rafrex/spa-github-pages.git
  • Remove the .git folder.
  • Delete the CNAME file
  • Create /docs folder in the root.
  • Move index.html, 404.html and /build into /docs

/docs/404.html

var segmentCount = 1;

/docs/index.html

<script src="/spa/build/bundle.js"></script>

/src/index.js

<BrowserRouter basename="/spa">

/package.json

"start": "webpack-dev-server --devtool eval-source-map --content-base docs/ --history-api-fallback --open --openPage spa/",

/webpack.config.js

output: {
  path: `${__dirname}/docs/build`,
  publicPath: '/spa/build/',
  filename: 'bundle.js',
},

Install React and dependencies. Build and start the webpack dev server.

npm install
npm run build
npm start

Opens: http://localhost:8080/spa/

Instantiate the repository and push to GitHub

git init
git add .
git init
git commit -m "first commit"
git remote add origin [email protected]:username/spa.git
git push -u origin master
  • Open the Settings page on GitHub
  • Choose GitHub Pages Source master branch /docs folder and Save
  • In a few minutes, open http://username.github.io/spa

Query params are garbled

The following URL with query:

http://localhost:8080/search?lang=English&q=abc&w=def&f=0101111100&p=10

is transformed into this:

http://localhost:8080/searc10?abc

I see that the script references q and p params of the query. Are these parameter names somehow reserved, beyond the scope of this script? Are they some params appended by GitHub?

npm module

Do you think it would be beneficial to others to have an npm module that does all of this? Something that an end-user will be able to install with yarn or npm and include in the top file a function that handles redirects? I would love to contribute to the creation of such a thing if you are interested to discuss

Images not rendering to website

Thanks for this repo! My app is up and running correctly except for the static images. I see them in the build, but dont understand why they are not rendering to the website. Are static images not supported?

My page is empty!!!

I did all the steps provided and I got the domain working " http://abdelrahman-mahmoud.me/ " but the page has no content. When I run npm start it works on my local machine but when I open the index.html page alone it doesn't work!!!
.
.
Any suggestions ??!!

Url with dashes don't seem to work

Thanks so much for putting this together, this is so helpful!

I implemented your solution and all urls work like a charm.

However, paths with dashes, for example example.com/some-path show me github's 404 page (not mine) and therefore don't redirect to the spa's route.

Doesn't work with Next.js

Hi sorry this is more of a question than a discrete issue - do you have any knowledge of whether this should work with Next.js? I've got it to a state where it is replacing the route in the URL (flashes by quickly) however the app itself just loads the index. I know Next handles routing a little differently to React, wondering if it's a lost cause. Can provide more info if you're interested in helping, otherwise feel free to delete this :-)

Some problem with service worker.

When I use create-react-app build app and enable service worker, the first visit to subpath (like: *.github.io/example) would use github default 404 page but custom 404.html.

['] being converted to [%27] and breaking React Router

Hey! Thanks a lot for the code. It seems to be working fine for the most parts but when the URL has " ' " in it, for example,

/editions/august'21-edition

it is being converted to

/editions/august%2721-edition

which is breaking the link.

Since I am using React Router,

      <Route path="/editions/august'21-edition" element={<><Publication edition={2} /></>} />

it is breaking the website.

You can view this issue yourself at https://the-innominate-newsletter.github.io/
You can try to click on any one of the cards and then try to refresh the page and you will see what I am talking about.

Thank you for taking the time to read this and helping me out!

Redirect stays on /?/

When I reload the page it always redirects me to domain/?/repository/subpath and not to the expected route domain/repository/subpath. I also tried creating a new repository with the modified index.html and 404.html, to rule out that it was not a problem with my repo, but I had the same problem: /?/

this.props.location.state is undefined when I refresh page

First of all Thanks for your amazing solution.
I am not sure about reason of this issue but whenever I refresh page I lost all of this.props.location.state objects. Funny thing is that it works on my local but location.state doesn't preserve on published page. Do you have any idea? thanks

Still cannot load my page in github pages

Hi

I got confused about your instructions with setting up 404.html in build folder. Every time I run npm build, the 404.html will be gone. I can load my page in
https://ptchiangchloe.github.io/portfolio-site/#/,
but every time I redirect my page, it will turn into 404 such as https://ptchiangchloe.github.io/#/art
so I need to manually add https://ptchiangchloe.github.io/portfolio-site/#/art in order to rendering.

Also, my custom URL doesn't work either. Here is my repo

Thanks,

Hanyu

Mention alternative of `HashRouter`

I was having problems with my SPA in Github Pages, and found a few solutions to the problem. This project fixes the problem in a roundabout way, and a slightly more direct solution is using HashRouter instead of BrowserRouter. I don't know if this fixes all instances of this problem, but it worked for me.

I think the readme should mention this alternative for folks who want a more lightweight solution.

Google search result contains "You need to enable JavaScript to run this app" in description

Hi

Thanks for this workaround. It works great for the page I recently created (https://jackfruit.games).

However, when I search for the page on google, I can see that in the description of the page, the error message "You need to enable JavaScript to run this app" is shown.

image
(Yes, I need to update the description of the main page too ๐Ÿ™ƒ)

The code for the page can be found here: https://github.com/jackfruitgames/jackfruit.games/blob/main/src/components/MarkdownContentPage.js

Any idea why this happens and how I can fix this?

? mark not being removed

In your examples the question mark (?) gets removed, but when I use it, the question mark stays in the URL.

How can I fix this?

Update README for further clarification of instructions

Hi,

I'm trying to implement this however am confused by the instructions, and hope you can provide further clarity.

I have an existing project that runs locally, however doesn't work properly when deployed to github pages due to react router conflict.

Of the detailed instructions, I clicked the link to go to version 6 (js option), and I followed steps 1-3, with the 'adding this boilerplate as the gh-pages branch of an existing repository' option.

When it gets to step 6, I get the below error.
Screen Shot 2022-10-11 at 9 53 33 am

In hindsight I see that these instructions don't ensure that the js version is downloaded run, following the instructions results in the typescript version.

So I scrapped it and restarted, this time manually downloading the zipped 6.0 package

I got to step 7 this time, before getting this fail error when pushing
Screen Shot 2022-10-11 at 10 19 20 am

And I realise that the SPA packages operates on the assumption that you haven't already tried to install onto gh-pages previously... many people only come to the SPA package after failing from usual method.

So I worked out I needed to push --force the last command.

Step 8 - confusing, I already have my own site, on the main branch. Are these instructions to be done on the gh-pages branch or the main branch? I follow these instructions assuming on the gh-pages branch.

I get a blank page on GH pages.

I try the instructions for local deployment, and it works to get the boiler plate content hosted locally... but how to I get my own site hosted? Do I merge main to gh-pages?

I read back over the instructions, this time look at the 'basic instructions' which say something different...

Does step 1 of basic instructions occur on main or gh-pages branch? Is this in addition to the detailed instructions or is it an alternative?

Finding bundle locally?

Hi Rafael,

Firstly thanks for making this resource!

I'm trying to test the site locally so I'll run npm start & open the html file and it won't find the bundle.js file:

GET file:///build/bundle.js net::ERR_FILE_NOT_FOUND index.html:94

It works fine on my github page domain (johnfiorilla.website). Can you offer some instruction on how to do this? Pretty new to webpack & stuff so I'd appreciate it! Thanks!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.