Deploy a Next.js Static Site to GitHub Pages

July 23, 2023

#github-pages#nextjs#react#static-site-generators#typescript#javascript

Let's say you've built an nice new static site using Next.js and you want to host it on GitHub Pages. Well there's a few steps to get it working, but it isn't too hard.

next js logo and github logo

This article assumes you already have a website built locally and you're just looking to host it on GitHub Pages. In the future I may write some how-tos for creating a blog using markdown and Next.js.

In this example I'm going to outline how I deploy to my main GitHub Pages repo for superflux.dev. It's hosted in my GitHub account's main Pages repository. Everyone has access to this special repo. Just create a new repo with the title your-github-username.github.io. GitHub will serve the static content there on your own subdomain https://your-github-username.github.io. You can even connect a custom domain like I did, I'll talk about that optionally at the end.

You can also use this tutorial to push repo-specific pages. Google for that if your unfamiliar.

Tl;dr

Static content

GitHub Pages can only serve static content. You need to be able to export your Next.js project to static code. You can do this by running

next build && next export

next export will export the static code for your project to the out/ directory. This is the code we're going to want to host on GitHub Pages.

To simplify and make it easy to remember I added this as a build script in my package.json. Add/edit the build script:

"build": "next build && next export"

Install gh-pages package

I've used this package in the past and it works well for GitHub Pages websites. gh-pages is a simple little tool that makes it easy to push projects to GitHub Pages repos or branches.

npm install gh-pages --save-dev

Branch setup

I store my website source in a separate repository, in my case a repo called personal-site-nextjs. By default gh-pages will deploy your static site to the origin remote. But we can change that. I like to keep the origin for my source repo pointing to the source remote.

  • personal-site-nextjs: repo containing the source code for my website. I do development here
  • tyrelh.github.io: my main GitHub Pages repo. This is were the compiled static assets are served from

You can also just use separate branches in the same GitHub Pages repository if you prefer. One for the source and one for the public assets. That feels messy to me so I prefer to keep them separate.

Deploy script

Next we'll setup a deploy script in our package.json to deploy our static content!

In your package.json create a new script. Remember it should be on the same level as the other scripts, in the "scripts" block:

"deploy": "gh-pages"

Now this by itself wont work yet, but I'm going to step you through each additional step to get the gh-pages command working. If you want to skip to the end and just see the final script, go ahead!

Static content directory

First you want to tell it what directory that your static assets are in. Remember this is the directory that next export in your build script outputs to. By default this is the out/ directory in your project. Make sure you run your build script first so that your static files are here.

Use the -d flag to set the directory.

"deploy": "gh-pages -d out

Remote & branch

We want to make sure gh-pages knows where it should push our static code to for hosting. It's good to set this up first so you don't accidentally push code somewhere you don't intend to.

For the gh-pages command, you can use the -b flag to tell it the remote branch you want to push to. Whatever the default branch of your username.github.io repo is will work. main or master for example.

"deploy": "gh-pages -d out -b main"

Next you'll want to set the remote. I like to hard code this in the script itself. Since it's going to be static for this project I don't see any reason not to. You can use the -r flag to set a remote URL. Copy the remote URL from your username.github.io repo.

"deploy": "gh-pages -d out -b main -r https://github.com/tyrelh/tyrelh.github.io.git"

Force Push to Remote

When deploying new static content to your GitHub Pages repo, generally you're going to want to overwrite what is there. At least this is the simplest way to do it. Before setting this just double check that your branch and remote are set correctly. You don't want to overwrite anything you don't intend to!

Use the -f flag to force the push to the remote, overwriting files and changes that may exist on the remote.

"deploy": "gh-pages -d out -b main -r https://github.com/tyrelh/tyrelh.github.io.git -f"

Ok! We're almost there! If this was a Jekyll project, or maybe a standard React project this would probably be all you need. But since we're using Next.js there is one more piece of config we need to do.

_next Hidden Folder

In your out/ directory of static assets, you should see a folder called _next/. This is where Next.js puts your compiled JavaScript and CSS.

By default, GitHub pages actually ignores folders beginning with a _. This is because GitHub Pages is setup to use Jekyll. Jekyll considers folders beginning with _ to be hidden folders, so GitHub Pages just doesn't serve them.

You can deploy the code as is, and it will look like it worked since you can see the _next/ directory in you username.github.io repo. But any requests to those assets when you visit your website will result in a 404 not found error.

To fix this you need to add a file named .nojekyll to the root of your username.github.io repo. You can do this right on the GitHub website to test it out. Once that file is there and you give it a couple minutes to take effect, your website should work without 404 errors!

But now if you deploy your static code again that file will get erased, breaking your website. We need to incorporate this file into our deploy or build scripts. I put it at the beginning of my deploy script, but you could also put it at the end of your build script too if that makes more sense to you.

We want to create that .nojekyll file in our out/ directory after our code is built, but before we deploy it using gh-pages.

At the beginning of your deploy script, add an echo command to create the file. The file can contain anything, I just echo the string true. Use the && between the echo and gh-pages commands so that they will run sequentially.

"deploy": "echo 'true' > ./out/.nojekyll && gh-pages -d out -b main -r https://github.com/tyrelh/tyrelh.github.io.git -f"

This wont quite work, because by default gh-pages ignores hidden dotfiles! To tell gh-pages you want to include dotfiles (that's files beginning with a .), use the -t flag on the gh-pages command.

"deploy": "echo 'true' > ./out/.nojekyll && gh-pages -d out -b main -r https://github.com/tyrelh/tyrelh.github.io.git -f -t"

And that's basically it!

Configure your repo for GitHub Pages

You'll need to make sure your repo is configured for Pages. Go to your repo, click on the repo Settings, and click Pages on the settings side bar. See the GitHub Pages docs for more general info about hosting static content.

[Optional] Configure a custom domain

After you follow the instructions here to setup your custom domain with your GitHub Pages website, you'll notice that you need a CNAME file in the root of your repo for the domain to map correctly.

Since our deploy process overwrites all the files in our GitHub Pages repo on each deploy, we need to add this file to our deploy/build process so it will be included each time. This is similar to what we needed to do for the .nojekyll file.

At the beginning of your deploy script, add another echo command. You want to output the root domain for your website to a file called CNAME. In my case my root domain is superflux.dev.

"deploy": "echo 'superflux.dev' > ./out/CNAME && echo 'true' > ./out/.nojekyll && gh-pages -d out -b main -r https://github.com/tyrelh/tyrelh.github.io.git -f -t"

Make sure to include the && between each command so they run sequentially!

Resources

Conclusion

And now that's it! To summarize we built a deploy script in our package.json that looks like this

"deploy": "echo 'true' > ./out/.nojekyll && gh-pages -d out -b main -r https://github.com/tyrelh/tyrelh.github.io.git -f -t"

You'll want to make sure your -b branch is pointing to the branch being served on your GitHub Pages repo, and that the -r remote is the URL of your GitHub Pages repo.

After building your website locally just run npm run deploy to push your static code to your GitHub Pages site! Remember the gh-pages package uses git to push code to the remotes, so you'll need to configure your username, email, and credentials to authenticate the push to your GitHub Pages repo.

Reach out to me on Twitter @tyreldelaney if you have any questions, comments, or suggestions!

Note: this article was written in April 2022. It sat for a while in my queue of articles as we were gearing up to have our first child. Hopefully nothing has changed drastically with Next.js and GitHub Pages that renders this info unusable.