Deploy an Nx React App to GitHub Pages
If you have an Nx React project on GitHub, do you want to have free hosting of this app? This blog shows how to deploy an Nx React app to GitHub pages. GitHub pages provides free hosting for static pages for a GitHub repository. I am going to show you how to:
- Deploy a sample flight search app
- Change base href
GitHub Repo: https://github.com/xiongemi/white-label-airline
GitHub Pages: https://xiongemi.github.io/white-label-airline
1. Install gh-pages
In your Nx repo, install gh-pages.
# npm
npm install gh-pages --save-dev# yarn
yarn add gh-pages --dev
2. Build the app with base href
For the deployed GitHub Pages, the URL would be:
http://<username>.github.io/<your repository name>
For example, this is the URL of my github repo: https://github.com/xiongemi/white-label-airline (username is xiongemi and respository name is white-label-airline), so the URL of my GitHub Pages would be https://xiongemi.github.io/white-label-airline.
Note: in the GitHub Pages URL, you will always get a base href that is the same as your GitHub repository name. So you need to specify the baseHref
in the build command. We could add the entire build command to package.json
’s scripts:
# npm
"predeploy": "npm run build -- --prod --baseHref=/<your repository name>/"# yarn
"predeploy": "yarn build && --prod --baseHref=/<your repository name>/"
If the app you want to host is not the default project in the workspace, you could use nx
command directly to build:
nx build <application name> --buildLibsFromSource --prod --baseHref=/<your repository name>/
The output compiled app is located in the outputPath
that is specified in the root folder’s workspace.json file.
In this case, the outputPath
is dist/apps/white-label-airline
. After the build is done, the compiled app is there:
If you examine the compiled index.html
, you should see that base href was overridden with the variable you passed in the command.
3. Change your router
Now with base href specified, you need to add this base href to your React Router
There are 2 ways of doing this:
- Use
<BrowserRouter>
with basename - Use
<HashRouter>
(recommended)
BrowserRouter with basename
You also need to specify the base href in the <BrowserRouter>
like:
<BrowserRouter basename={<your repository name>}>
To achieve that, we are going to pass in the base href to the browser router using environment variables.
We need to specify an environment variable such as NX_BASE_HREF
so we could access it directly process.env.NX_BASE_HREF
. So the build command would be:
# npm
NX_BASE_HREF=/<your repository name>/ npm run build -- --prod --baseHref=/<your repository name>/# yarn
NX_BASE_HREF=/<your repository name>/ yarn build && --prod --baseHref=/<your repository name>
When we define the routes of the app, we specify the basename
to be process.env.NX_BASE_HREF
:
<BrowserRouter basename={process.env.NX_BASE_HREF}>
Note: for Nx’s environment variable, it has to start with the prefix NX_
.
So the new predeploy
command would be:
# npm
"predeploy": NX_BASE_HREF=/<your repository name>/ npm run build -- --prod --baseHref=/<your repository name>/# yarn
"predeploy": "NX_BASE_HREF=/<your repository name>/ yarn build && --prod --baseHref=/<your repository name>/"
However, this way has a limitation that it does not work if you refresh or enter URL manually: https://stackoverflow.com/questions/27928372/react-router-urls-dont-work-when-refreshing-or-writing-manually.
To overcome this, we could use <HashRouter>
.
HashRouter
In your routes file, use <HashRouter>
, no need to specify the basename
.
// Before using BrowserRouter:
<BrowserRouter basename={...}>
<Switch>
<Route path={...}>
...
</Route>
....
</Switch>
</BrowserRouter>// After using HashRouter:
<HashRouter>
<Switch>
<Route path={...}>
...
</Route>
....
</Switch>
</HashRouter>
connected-react-router
connected-react-router
is a library that syncs router with the redux state. If you are using this library, the code becomes:
// BrowseRouter
const history: History = createBrowserHistory({
basename: process.env.NX_BASE_HREF,
});// HashRouter
const history: History = createHashHistory();<ConnectedRouter history={history}>
<Switch>
<Route path={...}>
...
</Route>
...
</Switch>
</ConnectedRouter>
4. Deploy the app
To deploy the app, add these commands in package.json’s scripts:
"deploy": "gh-pages -d <outputPath>"
This outputPath
passed in here is whatever specified in the workspace.json for your app. For example
Run npm run deploy
and you should see this the terminal:
In your GitHub repo, you should see a branch named “gh-pages” with the content of your build:
In the GitHub repo settings, you should see it points to the “gh-pages” branch:
Now you could access the GitHub pages at the URL http://<username>.github.io/<your repository name>
.
Make sure to specify the homepage
in package.json
to your Github Pages URL.
"homepage": "http://xiongemi.github.io/white-label-airline",
This is what my package.json
loos like:
{
"name": "white-label-airline",
"version": "0.0.0",
"license": "MIT",
"homepage": "http://xiongemi.github.io/white-label-airline",
"repository": {
"type": "git",
"url": "https://github.com/xiongemi/white-label-airline.git"
},
"scripts": {
"predeploy": "npm run build -- --prod --baseHref=/white-label-airline/",
"deploy": "gh-pages -d dist/apps/white-label-airline"
}
}
5. Debug locally
If you need to debug your production build locally, you need to install http-server:
# npm
npm install --global http-server# yarn
yarn global add http-server
Run predeploy
command to build the app and run:
http-server <outputPath>
The production build app should be served on “http://127.0.0.1:8080/<repository name>”.
In my example, the command is http-server dist/apps
and the app is servered on http://127.0.0.1:8080/white-label-airline
.
These are the steps you need to take to deploy an Nx React repo without changing the base href in your local development environment. If you run npm run start
or yarn start
locally, notice the base href is empty.
Now you got a deployed app with free hosting. :)