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. 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.
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.
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.
- Create a
.travis.yml
file in the top directory of your Github repository. - 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:
# 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
-
Once you’ve added the
.travis.yml
to your repository, you need to add thegithub_token
(needed to push to yourgh-pages
branch) variable to your Travis CI settings.- 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 thepublic_repo
scope. The Full GitHub repo scope - Open
https://travis-ci.com/github/{your_username}/{your_repository}
in a browser. - Navigate to more options -> Setting. Travis Settings
- Once there add a new
environment variable
calledgithub_token
and use your access token as thevalue
. Optionally make it only available on the master branch. Travis Settings Environment Variable
- 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
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.
You should be able to see the gh-pages
branch.
To enable GitHub Pages for your repository you need to:
- Navigate to the settings tab for your Github repository (such as https://github.com/james-wallis/wallisconsultancy/settings)
- Scroll down to the “GitHub Pages” section.
- Under the source tab select
gh-pages branch
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:
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 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:
- Open or create a next.config.js file.
- Add an
assetPrefix
to yourmodule.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:
- Open or create a next.config.js file.
- 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',
},
}
- 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:
- Open or create a next.config.js file.
- Add a
basePath
to yourmodule.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!
Here's the link to Wallis Consultancy again to see the result of the above steps!
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.
Top comments (12)
Thanks for the guide! I also had trouble with CSS, but it was because by default Github Pages ignores the
_next
directory because it assumes names prefixed with underscore are Jekyll files. You can add a.nojekyll
file to the root of your Nextpublic
directory (so it ends up in the GH Pages root) to disable Jekyll processing.I use
gh-pages
locally to deploy to Github, and to get that to deploy the.nojekyll
dotfile, you also have to add the--dotfiles
option to it. So in mypackage.json
, my deploy script ends up being"deploy": "gh-pages --dotfiles --dist out"
.Cheers!
Good to know! Thanks Travis.
Hi James!
I was able to follow your walk through pretty cleanly. However, it seems to have published my readme to my github pages instead of my nextjs app. Do you have any insight?
Hi Nicole! I just had a look at your repository and it looks like you're using the
main
branch whereas your.travis.yml
is saying to only deploy the Next.js application onmaster
branch. Could you try changing this line: github.com/Sun-Mountain/Sun-Mounta... to bemain
instead ofmaster
.Then I think you'll need to change the GitHub pages source to be the
gh-pages
branch (which also hasn't been created yet due to the issue above).Let me know if that helps and I'll update the tutorial.
Thanks!
Hi James!
Thank you for your prompt reply! And thank you for pointing out the main, it's a new change so I'm still getting used to it. 🤦♀️
And it worked! Thank you so much! <3
Good to hear, your website looks great!
Thank you! And one more quick question. For updating the website, would you recommend branching off of master/main and then merging main into gh-pages? Or branching of gh-pages and merging into gh-pages and then main?
So you should branch off master/main and then merge back into master/main.
Once you've merged your changes, Travis will build and export your Next.js application and then push the new HTML (
out
directory) onto yourgh-pages
branch - it uses a force push to completely overwrite the branch.You shouldn't ever need to touch the
gh-pages
branch.The image in the right side doesn't show appropriately.
james-wallis.github.io/wallisconsu...
Thanks for pointing it out! I'll take a look soon - it is likely something to do with my Netlify CMS configuration.
thank you man, I followed your post, and it all good!
Cheers Moussa! Thanks for the feedback