Skip to content

Commit

Permalink
Convert to spa-github-pages repo
Browse files Browse the repository at this point in the history
  • Loading branch information
rafgraph committed May 14, 2016
1 parent 51aedca commit 5dc767e
Show file tree
Hide file tree
Showing 9 changed files with 25,978 additions and 145 deletions.
2 changes: 1 addition & 1 deletion 404.html
Expand Up @@ -2,7 +2,7 @@
<html>
<head>
<meta charset="utf-8">
<title>React for GitHub Pages</title>
<title>Single Page Apps for GitHub Pages</title>
</head>
<body>
<script type="text/javascript">
Expand Down
2 changes: 1 addition & 1 deletion CNAME
@@ -1 +1 @@
react-github-pages.rafrex.com
spa-github-pages.rafrex.com
100 changes: 4 additions & 96 deletions README.md
@@ -1,106 +1,14 @@
# React for GitHub Pages
[Live example](http://react-github-pages.rafrex.com)
# Single Page Apps for GitHub Pages

React for GitHub Pages is a lightweight solution for deploying [React][react] single page apps with [React Router][reactRouter] `browserHistory` using [GitHub Pages][ghPagesOverview].
[Live example][liveExample]

I love React with React Router. Need complex views with lifecycle methods and dynamic routing? Done. Need a simple site with a few fixed routes? Use functional stateless components and JSX. Done. And I love GitHub Pages. `git push` on a `gh-pages` branch and it's live on a great CDN (for free), does it get any easier than that? But unfortunately GitHub Pages doesn't play nice with React, until now...
This is a lightweight solution for deploying single page apps with [GitHub Pages][ghPagesOverview]. You can easily deploy a [React][react] single page app with [React Router][reactRouter] `browserHostory`, like the one in the [live example][liveExample], or a single page app built with any frontend library.

##### Why it's necessary
GitHub Pages is a static server that doesn't support single page apps. When there is a fresh page load for a url like `example.tld/foo`, where `/foo` is a frontend route, GitHub Pages returns a 404 because it knows nothing of `/foo`. This only affects fresh page loads, however, as navigating to `/foo` from within the single page app is not a problem because the server never receives a request for `/foo`. (Note you could use `hashHistory` instead, but really no one wants to use `hashHistory` with its janky urls and incompatibility with `#hash-fragments`, e.g. `example.tld/foo` becomes `example.tld/#/foo?_k=yknaj`, ugh).

Also, [GitHub Pages are always available at `example.tld/my-repo-name`][ghPagesMyRepoName], even when a custom domain is in use. Accessing the site at `/my-repo-name` will cause frontend routing to break, so when the site is accessed at `/my-repo-name`, a redirect to just the domain with no path is required.

##### 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][404html] that takes the current url and converts the path, query string and hash fragment into just a query string, and then redirects the browser to the new url with only a query string and no path or hash fragment. For example, `example.tld/one/two?a=b&c=d#qwe`, becomes `example.tld/?redirect=true&pathname=%2Fone%2Ftwo&query=a=b%26c=d&hash=qwe`.

The GitHub Pages server receives the new request, e.g. `example.tld?redirect=true...`, ignores the query string and returns the `index.html` file, which loads the React single page app. The [root React Router route][onEnterRedirect] has an `onEnter` hook that calls a function to check for a redirect in the query string. If a redirect is present in the query string, it's converted back into the correct url and React Router redirects to the url which loads the respective routes and components. (Note that these redirects are only needed with fresh page loads, and not when navigating within the single page app once it's loaded).

The other issue of GitHub Pages always being available at `/my-repo-name` is handled by the same [root route onEnter hook][onEnterRedirect] function, which checks for the `/my-repo-name` path and redirects to just the domain (with no path) if needed.


## Usage instructions
*For general information on using GitHub Pages please see [GitHub Pages Basics][ghPagesBasics], note that pages can be [User, Organization or Project Pages][ghPagesTypes]*

1. Clone this repo (`$ git clone https://github.com/rafrex/react-github-pages.git`)
2. Delete the `.git` directory (`cd` into the `react-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 `react-github-pages` directory, and then `$ git add .` and `$ git commit -m "Add React for GitHub Pages boilerplate"` to initialize a fresh repository
- If this will be a Project Pages site, then change the branch name from `master` to `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 `react-github-pages` directory to anything you wnat (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 from the working directory of your existing repo (`$ git rm -rf .`)
- Copy all of the files and directories from the cloned `react-github-pages` directory into your project's now empty working directory
- `$ git add .` and `$ git commit -m "Add React for GitHub Pages boilerplate"` to instantiate the `gh-pages` branch
4. Set up your custom domain - see GitHub Pages instructions for [setting up a custom domain][customDomain]
- *Note that you must use a custom domain if you are setting up a Project Pages site in order for GitHub Pages to serve the custom 404 page, however, if you are creating a User or Organization Pages site, then using a custom domain is optional (if you don't use a custom domain delete the `CNAME` file)*
- Update the [`CNAME` file][cnameFile] with your custom domain, don't include `http://`, 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 `http://`)
5. [Set your repo name in index.js][setRepoName], this should match your repository name as it is listed on GitHub
6. [Set your domain name in index.js][setDomain], if you are using a custom domain then this should match the domain in your `CNAME` file (except include the `http://`), if you are not using a custom domain, then this will be `http://<your github username or orgname>.github.io`
7. Run `$ npm install` to install React and other dependencies, and then run `$ webpack` to update the build
8. `$ 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 master` for User or Organization Pages)

The example site should now be live on your domain

##### Creating your own site
- Write your own React components, create your own [routes][routes], and add some style!
- Change the [title in `index.html`][indexHtmlTitle] and the [title in `404.html`][404htmlTitle] to your site's title, also [remove the Google analytics script][googleAnalytics] from the header of `index.html` (the analytics function is wrapped in an `if` statement so that it will only run on the example site's domain (http://react-github-pages.rafrex.com), but you don't need it, so remove it or replace it with your own analytics)
- Change the readme and license as you see fit
- After you update your code run `$ webpack` (or `$ webpack -p` for [production][webpackProduction], or `-d` for [development][webpackDevelopment]) to update the build, then `$ git commit` and `$ git push` to make your changes live

##### Miscellaneous
- The `.nojekyll` file in this repo [turns off Jekyll for GitHub Pages][nojekyll]
- Need form submission on your static site? Use [Formspree][formspree]
- One of the awesome 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


## Want to help?
- Fork this repo and port the boilerplate over to another frontend framework
- This should be pretty straight forward as the [`404.html`][404html] page and most of the logic in [`index.js`][indexjs] can remain the same
- You'll have to figure out the equivalent of:
- Checking if a redirect is required before loading the app (the equivalent of the [`checkForRedirect` onEnter hook][onEnterRedirect])
- Accessing the query string from within the framework (the equivalent of [`nextState`][nextState], alternatively you could get this from `window.location.search`)
- Using the framework's router to goto the correct path after parsing the redirect query (the equivalent of [`replace(redirectTo)`][redirectTo], note that you can't use `window.location.replace` as that will make a request to GitHub Pages' static server, which will return the 404 page and start an infinite redirect loop)

Pull requests welcome. Please open [issues][issues] to report bugs.
Thoughts, questions, suggestions? Contact me via [email][email] or [twitter][twitter].





<!-- links to within repo -->
[indexjs]: https://github.com/rafrex/react-github-pages/blob/gh-pages/index.js
[nextState]: https://github.com/rafrex/react-github-pages/blob/gh-pages/index.js#L20
[redirectTo]: https://github.com/rafrex/react-github-pages/blob/gh-pages/index.js#L60
[setRepoName]: https://github.com/rafrex/react-github-pages/blob/gh-pages/index.js#L74
[setDomain]: https://github.com/rafrex/react-github-pages/blob/gh-pages/index.js#L77
[routes]: https://github.com/rafrex/react-github-pages/blob/gh-pages/index.js#L84
[onEnterRedirect]: https://github.com/rafrex/react-github-pages/blob/gh-pages/index.js#L86
[indexHtmlTitle]: https://github.com/rafrex/react-github-pages/blob/gh-pages/index.html#L6
[googleAnalytics]: https://github.com/rafrex/react-github-pages/blob/gh-pages/index.html#L9
[404html]: https://github.com/rafrex/react-github-pages/blob/gh-pages/404.html
[404htmlTitle]: https://github.com/rafrex/react-github-pages/blob/gh-pages/404.html#L5
[cnameFile]: https://github.com/rafrex/react-github-pages/blob/gh-pages/CNAME
[issues]: https://github.com/rafrex/react-github-pages/issues

<!-- links to github docs -->
[ghPagesOverview]: https://pages.github.com/
[ghPagesBasics]: https://help.github.com/categories/github-pages-basics/
[ghPagesTypes]: https://help.github.com/articles/user-organization-and-project-pages/
[customDomain]: https://help.github.com/articles/quick-start-setting-up-a-custom-domain/
[nojekyll]: https://help.github.com/articles/files-that-start-with-an-underscore-are-missing/
[ghPagesMyRepoName]: https://help.github.com/articles/custom-domain-redirects-for-github-pages-sites/

<!-- other links -->
[liveExample]: http://spa-github-pages.rafrex.com
[react]: https://github.com/facebook/react
[reactRouter]: https://github.com/reactjs/react-router
[webpackProduction]: https://webpack.github.io/docs/cli.html#production-shortcut-p
[webpackDevelopment]: https://webpack.github.io/docs/cli.html#development-shortcut-d
[formspree]: http://formspree.io/
[email]: mailto:code@rafrex.com
[twitter]: https://twitter.com/rafrrex
25,973 changes: 25,959 additions & 14 deletions __build__/bundle.js

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions components/App.jsx
Expand Up @@ -23,11 +23,11 @@ function App({ children, routes }) {

return(
<div style={{maxWidth: '500px'}}>
<h2 style={{marginBottom: 0}}>React for GitHub Pages</h2>
<a href="https://github.com/rafrex/react-github-pages"
<h2 style={{marginBottom: 0}}>Single Page Apps for GitHub Pages</h2>
<a href="https://github.com/rafrex/spa-github-pages"
style={{marginBottom: '1em', display: 'block'}}
>
https://github.com/rafrex/react-github-pages
https://github.com/rafrex/spa-github-pages
</a>
<nav>
{generateMapMenu()}
Expand Down
7 changes: 3 additions & 4 deletions components/Home.jsx
Expand Up @@ -5,17 +5,16 @@ function Home() {
return (
<div>
<p>
This is an example single page app using the React for GitHub Pages
boilerplate. It is built with React and React Router using
This is an example single page app built with React and React Router using
browserHistory. Navigate with the links below and refresh the page or
copy/paste the url to test out the redirect functionality deployed to
overcome GitHub Pages incompatibility with single page apps
(like this one).
</p>
<p>
Please see <a href="https://github.com/rafrex/react-github-pages#readme">
Please see <a href="https://github.com/rafrex/spa-github-pages#readme">
the repo readme</a> for instructions on how to use this boilerplate to deploy your
own React project using GitHub Pages.
own single page app using GitHub Pages.
</p>
<div><Link to="/example">Example page</Link></div>
<div><Link to="/example/two-deep?field1=foo&field2=bar#boom!">
Expand Down
21 changes: 1 addition & 20 deletions index.html
Expand Up @@ -3,26 +3,7 @@
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>React for GitHub Pages</title>

<!-- Start Google Analytics -->
<script>
if (window.location.hostname === 'react-github-pages.rafrex.com') {
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-75627125-3', 'auto');
ga('send', 'pageview');
} else {
console.warn(
'WARNING: the Google analytics function is for a different ' +
'domain (http://react-github-pages.rafrex.com) and did not run, ' +
'please remove the script from the index.html file.');
}
</script>
<!-- End Google Analytics -->

<title>Single Page Apps for GitHub Pages</title>
</head>
<body>
<div id="root"></div>
Expand Down
4 changes: 2 additions & 2 deletions index.js
Expand Up @@ -71,10 +71,10 @@ function parseRedirectQuery(query, replace) {
// a redirect to the domain with no path is required.
// https://help.github.com/articles/custom-domain-redirects-for-github-pages-sites/
// SET THIS: e.g. my-repo-name
const gitHubRepoName = 'react-github-pages';
const gitHubRepoName = 'spa-github-pages';
// The domain for your site
// SET THIS: e.g. http://subdomain.example.tld, or http://www.example.tld
const domain = 'http://react-github-pages.rafrex.com';
const domain = 'http://spa-github-pages.rafrex.com';
function redirectToDomain() {
window.location.replace(domain);
}
Expand Down
8 changes: 4 additions & 4 deletions package.json
@@ -1,18 +1,18 @@
{
"name": "react-github-pages",
"description": "React with React Router boilerplate for GitHub Pages",
"description": "Single Page Apps for GitHub Pages",
"author": "Rafael Pedicini <code@rafrex.com>",
"license": "MIT",
"private": "true",
"main": "index.js",
"repository": {
"type": "git",
"url": "git+https://github.com/rafrex/react-github-pages.git"
"url": "git+https://github.com/rafrex/spa-github-pages.git"
},
"bugs": {
"url": "https://github.com/rafrex/react-github-pages/issues"
"url": "https://github.com/rafrex/spa-github-pages/issues"
},
"homepage": "https://github.com/rafrex/react-github-pages#readme",
"homepage": "https://github.com/rafrex/spa-github-pages#readme",
"dependencies": {
"babel-core": "^6.8.0",
"babel-loader": "^6.2.4",
Expand Down

0 comments on commit 5dc767e

Please sign in to comment.