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
Deploy Nx React app with GitHub Pages

GitHub Repo: https://github.com/xiongemi/white-label-airline

GitHub Pages: https://xiongemi.github.io/white-label-airline

Screenshot of the GitHub Pages

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": "build -- --prod --baseHref=/<your repository name>/"
# yarn
"predeploy": "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.

outputPath in workspace.json

In this case, the outputPath is dist/apps/white-label-airline. After the build is done, the compiled app is there:

Built Application under /dist/apps

If you examine the compiled index.html, you should see that base href was overridden with the variable you passed in the command.

compiled index.html

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:

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:

gh-pages branch

In the GitHub repo settings, you should see it points to the “gh-pages” branch:

Settings for GitHub Pages

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 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. :)

A frontend web developer in Toronto