loading...

Deploying a Next.js app to GitHub Pages

jameswallis profile image James Wallis ・7 min read

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.

The finished website (hosted on GitHub Pages): https://wallisconsultancy.co.uk
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

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

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 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):
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:
    branch: master # Only deploy when the build is on master branch

For more information the official Travis Github Pages docs

  1. 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 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 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 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 Travis Settings Environment Variable
  2. Now that you've setup 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 setup 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
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 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
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 it’s dependencies or link to other pages.

You can recreate this locally by running next export and then using 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.

 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.

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.
  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:
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.
  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:
module.exports = {
    env: {
        BACKEND_URL: '/wallisconsultancy',
    },
}
  1. Modify your <Link> components to use the prefix by changing them to be:
<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

href="/about"

to

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. To use it all you need to do is:

  1. Open or create a next.config.js file.
  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:
module.exports = {
    basePath: '/wallisconsultancy',
}

Final next.config.js

Combining the assetPrefix and basePath my next.config.js is:

module.exports = {
    basePath: '/wallisconsultancy',
    assetPrefix: '/wallisconsultancy/',
}

Bonus: With next-optimized-images
In a previous blog post I introduced 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 variable to my next.config.js. With this fix it now looks like this:

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!

Want to use a custom domain?

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.

Discussion

pic
Editor guide