Deploying a Next.js app to GitHub Pages
Tags: github, react, javascript, nextjs
Published: 2020-08-07 14:39:40

*This blog is part of a series where I document rebuilding a website that relies on HTML, CSS and Bootstrap in React.js using the Next.js framework to improve performance, reduce costs and increase my workflow for future changes.*

> **2021 Update**
> *I have moved Wallis Consultancy from GitHub Pages to Vercel. [I wrote a post describing my motivations for doing so which you can read here](https://dev.to/jameswallis/launching-my-personal-next-js-site-on-vercel-in-less-than-a-minute-296h). Essentially, Next.js integrates with Vercel a lot better than with GitHub Pages.*
*I've kept a version of Wallis Consultancy hosted on GitHub pages for this blog and have updated all links to Wallis Consultancy below.*
*Having said that, GitHub Pages is 100% still a good place to host your Next.js project!*

The finished website (hosted on GitHub Pages): https://james-wallis.github.io/wallisconsultancy/
The source code: https://github.com/james-wallis/wallisconsultancy

## Intro

The re-implementation of Wallis Consultancy into a Next.js application is complete. This blog post documents the process of taking a Next.js project and hosting it on GitHub pages. It covers:
* Using `next export` to convert the Next.js project to a static website.
* Building a Travis pipeline to build the website and push it to a `gh-pages` branch.

## Overview of technologies
### GitHub Pages
> GitHub Pages is a static site hosting service that takes HTML, CSS, and JavaScript files straight from a repository on GitHub optionally runs the files through a build process and publishes a website. 

[GitHub Pages](https://pages.github.com/)

### Travis
> Travis CI is a hosted continuous integration service used to build and test software projects hosted at GitHub and Bitbucket.

It’s free for open-source projects and integrates automatically with Github. All you need to do is sign up and add a `.travis.yml` file and it’s ready to go.

[Travis CI](https://travis-ci.org/)

### Next.js export 
> `next export` allows you to export your app to static HTML, which can be run standalone without the need of a Node.js server.

It generates the HTML into an `out` directory. From there you can use tools such as [serve](https://www.npmjs.com/package/serve) to run your app.


Now that the technologies used in this blog have been introduced, let's deploy our Next.js app to GitHub Pages.


## Creating the Travis build
Connecting Travis to a GitHub repository is as simple as creating a `.travis.yml`. The following documents this process and how to use secret environment variables with a Travis build.

1. Create a `.travis.yml` file in the top directory of your Github repository.
2. Add the following (without the comments):
```yaml
language: node_js # Node.js based project
node_js:
  - 12 # Level of Node.js to use
cache:
  directories:
  - node_modules # Cache the node_modules folder for quicker build times
script:
  - npm run build # Runs next build
  - npm run export # Runs next export and generates the out directory
  - touch out/.nojekyll # Creates a file telling Github not to build the project using Jekyll
deploy:
  provider: pages # Informs Travis this is a deployment to GitHub Pages
  skip_cleanup: true # Prevents Travis from resetting the working directory made during the build
  github_token: $github_token # GitHub access token to use when pushing to the gh-pages branch
  local_dir: out # Directory to push to the gh-pages branch
  on:
    # Only deploy when the build is on master or main branch - two common default branch names
    # If you're using a different branch name, add it here
    all_branches: true
    condition: $TRAVIS_BRANCH =~ ^(master|main)$

```
For more information the [official Travis Github Pages docs](https://docs.travis-ci.com/user/deployment-v2/providers/pages/)

3. Once you’ve added the `.travis.yml` to your repository, you need to add the `github_token` (needed to push to your `gh-pages` branch) variable to your Travis CI settings. 
  1. First get an API token following the instructions on [Creating a personal access token - GitHub Docs](https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token) *Note*: As my repository was private while making this blog I enabled the whole `repo` scope. However you may be able to just enable the `public_repo` scope.
![GitHub Repo Scope for access token](https://dev-to-uploads.s3.amazonaws.com/i/ogiqsi8d63glc59p6a4g.jpg)
*The Full GitHub repo scope*

  2. Open `https://travis-ci.com/github/{your_username}/{your_repository}` in a browser.
  3. Navigate to more options -> Setting.  
![Travis Settings](https://dev-to-uploads.s3.amazonaws.com/i/av9vyftpt2sl5t843fph.jpg)
*Travis Settings*
  4. Once there add a new `environment variable` called `github_token` and use your access token as the `value`. Optionally make it only available on the master branch.
![Travis Settings Environment Variable](https://dev-to-uploads.s3.amazonaws.com/i/6f84pfcxgpxmo8ccivgz.jpg)
*Travis Settings Environment Variable*

4. Now that you've set up the Travis settings and `.travis.yml` you're ready to start your first Travis build. To do this, publish your new `.travis.yml` to your master branch and it will start automatically. If you’ve already done this, start a new build of master from the Travis-ci UI.

Phew, that was a lot of configuring, but it's done. Let's set up GitHub Pages so that the website will be viewable.

## Setup GitHub Pages
By this point, the Travis build should have successfully completed and created a `gh-pages` branch in your repository. This means that the static website code is available and just needs to be served somewhere such as GitHub Pages.

![GitHub branch overview](https://dev-to-uploads.s3.amazonaws.com/i/08rfqglhw7huf4r0036u.jpg)
*You should be able to see the `gh-pages` branch.*

To enable GitHub Pages for your repository you need to:
1. Navigate to the settings tab for your Github repository (such as https://github.com/james-wallis/wallisconsultancy/settings) 
2. Scroll down to the “GitHub Pages” section.
3. Under the source tab select `gh-pages branch`
![GitHub Pages settings](https://dev-to-uploads.s3.amazonaws.com/i/oaq9ld9k5ucazxsu30np.jpg)
*The GitHub Pages settings*

In a little while, you should be able to access your website at the URL provided by GitHub (if you can’t go back over the Travis-CI steps above). That's all the setup that is needed to host a static site with GitHub pages.

Or is it...

### Something isn't quite right... where's the CSS styling
If you followed both sections above you will be expecting to see your website as it looked on your local machine.

Instead you will likely be greeted with a website with the correct content, but no styling. Additionally, if you try to navigate between pages, they will not resolve. It'll look something like the below:

![Broken Wallis Consultancy site](https://dev-to-uploads.s3.amazonaws.com/i/flj94q6oybmv6rxlbclr.jpg)
*Wallis Consultancy website without the CSS*

**Why is this happening you ask?**
Next.js expects the CSS, JavaScript files and Images to be hosted on `user.github.io/` but in the case of GitHub pages, the site will be hosted on a subpath, in my case `user.github.io/wallisconsultancy`. This results in the website not being able to find any of its dependencies or link to other pages.

You can recreate this locally by running `next export` and then using [serve](https://www.npmjs.com/package/serve) to serve the parent directory of your output directory (usually `out`). So for me `serve wallisconsultancy` where the output directory is `wallisconsultancy/out`.


**Ok fine, but can we fix it?**

Yes of course!

*Note: If you’re going to host on a custom domain this problem will disappear (as long as you are not using a subpath like GitHub pages). Skip the rest of this blog and read my next blog: [Using a custom domain with GitHub Pages](https://dev.to/jameswallis/using-a-custom-domain-with-github-pages-1g1m).*

### Next.js assetPrefix and basePath to the rescue

This next section will be split into two subsections. The first will focus on fixing the CSS styling and other assets such as images using `assetPrefix`. The second will focus on fixing links to different pages, first using an environment variable to prefix the route and secondly using `basePath`, a new configuration variable introduced in [Next.js 9.5](https://nextjs.org/blog/next-9-5#customizable-base-path).

#### Fixing CSS and other assets

Fixing CSS and other assets is simple and can be done in only a few steps:
1. Open or create a [next.config.js file](https://nextjs.org/docs/api-reference/next.config.js/introduction).
2. Add an `assetPrefix` to your `module.exports` with the value of your GitHub pages subpath with a forward slash at either side. For me this is:
```javascript
module.exports = {
    assetPrefix: '/wallisconsultancy/',
}
```

With that simple change, you should be able to push that change to GitHub pages and will be able to see the page layout that you expect.

#### Fixing Links between pages

**Next.js 9.4 and below**
Prior to Next.js 9.5, fixing the page links meant modifying each `<Link>` that you had created to have a `prefix`. The cleanest way of achieving this is to:
1. Open or create a [next.config.js file](https://nextjs.org/docs/api-reference/next.config.js/introduction).
2. Add an environment variable called `BACKEND_URL` with the value of your GitHub Pages subpath with a forward slash at the start. For me this is:
```javascript
module.exports = {
    env: {
        BACKEND_URL: '/wallisconsultancy',
    },
}
```
3. Modify your `<Link>` components to use the prefix by changing them to be:
```javascript
<Link href={`${process.env.BACKEND_URL}${href}`}>{href}</Link>
```
  So for a link to the about page the `href` for the `<Link>` would change from 
```javascript
href="/about"
``` 
to 
```javascript
href={`${process.env.BACKEND_URL}/about`}
```

This is a bit messy, but fortunately, in Next.js 9.5 this was simplified with the introduction of a `basePath` variable.

**Next.js 9.5 and above**
Instead of adding a `BACKEND_URL` to every `<Link>`, [Next.js 9.5 introduces the basePath variable](https://nextjs.org/blog/next-9-5#customizable-base-path). To use it all you need to do is:
1. Open or create a [next.config.js file](https://nextjs.org/docs/api-reference/next.config.js/introduction).
2. Add a `basePath` to your `module.exports` with the value of your GitHub pages subpath with a forward slash at the start. For me this is:
```javascript
module.exports = {
    basePath: '/wallisconsultancy',
}
```

#### Final `next.config.js`
Combining the `assetPrefix` and `basePath` my `next.config.js` is:
```javascript
module.exports = {
    basePath: '/wallisconsultancy',
    assetPrefix: '/wallisconsultancy/',
}
```

*Bonus: With `next-optimized-images`*
In [a previous blog post](https://dev.to/jameswallis/seo-and-image-optimization-with-next-js-23m9) I introduced [next-optimized-images](https://github.com/cyrilwanner/next-optimized-images) which can be used to improve the performance of a website by compressing the images.

To fix the GitHub Pages subpath issue with it I added the [imagesPublicPath](https://github.com/cyrilwanner/next-optimized-images#imagespublicpath) variable to my `next.config.js`. With this fix it now looks like this:
```javascript
const withPlugins = require('next-compose-plugins');
const optimizedImages = require('next-optimized-images');
module.exports = withPlugins([
  [optimizedImages, {
    mozjpeg: {
      quality: 80,
    },
    pngquant: {
      speed: 3,
      strip: true,
      verbose: true,
    },
    imagesPublicPath: '/wallisconsultancy/_next/static/images/',
  }],
  {
    basePath: '/wallisconsultancy',
    assetPrefix: '/wallisconsultancy/',
    env,
  },
]);
```

And with that, my website is hosted on GitHub pages, looks good and I can navigate between pages as I expect. You are now able to show your website to anyone around the world!

[Here's the link to Wallis Consultancy again to see the result of the above steps!](https://james-wallis.github.io/wallisconsultancy/)

[Want to use a custom domain?](https://dev.to/jameswallis/using-a-custom-domain-with-github-pages-1g1m)

## Roundup
In this blog, I demonstrated how to build a Travis build that will build and export your Next.js application into a static website. I then configured GitHub pages to host the website and fixed CSS and link problems due to the subpath it hosts websites on.

In the next and final blog of this series, I'll show you how to use a custom domain with GitHub Pages.