A Swift Blog.

Hosting a NextJS blog on Github Pages

Cover Image for Hosting a NextJS blog on Github Pages

It was a task I'd been putting off for years: Getting my own blog up and running. I had been blogging on Medium, but I wanted more control. I decided to kill two (or actually, three?) birds with one stone: host my own blog, learn the basics of React, and see what NextJS is all about. If you're reading this, it means all three of those worked out 😉 But that doesn't mean it was without hurdles.

In this article I'll describe steps you can follow if you want to host a statically generated blog on Github Pages. A nice little bonus: this will be 100% free. We'll go over the following:

  • Checking out a Markdown powered NextJS blog template
  • Export static HTML with NextJS and Github Actions
  • Setting up Github Pages correctly
  • How to resolve any issues that come up

Getting started

Let's start with checking out the blog template and finding our way around it. For this tutorial we'll use the blog-starter-typescript template from the official NextJS repository. You can read more about it here. To use that as a template, run the following command in your terminal:

npx create-next-app --example blog-starter-typescript <app name here>

After that has finished running, open the newly created directory in your code editor of choice. This is a completely functional NextJS application, but for this tutorial we'll only concern ourselves with the following directories:

  • _posts: This is where you'll find the posts that are shown on the blog.
  • public/assets: This is where you add images that can be used on your blog.

Note: you'll see there is currently a convention to create new folders in public/assets/blog/ for each article you create, but you're free to do whatever you like. Anything in the public folder will be accessible by their path, relative to the public folder, e.g. assets/blog/preview/cover.jpg.

Run a quick npm run dev in the terminal, go to http://localhost:3000/ and tada ğŸŽ‰ There's your blog! Though I'll admit, it looks eerily similar to this one... You might want to change the styling a bit 😬

Currently you're serving this blog through a webserver that is run locally on your device. While this opens up a lot of possibilities (dynamic content, server-side rendering), this is not what we need! To serve our blog on Github Pages we need a statically generated website. Good thing there's this thing called

Static HTML export

This is a way to export our project to static HTML (😝), which can be run without a Node.js server. That's exactly what we need! Open the package.json file in your blog project, and add the following entry to the "scripts" object:

"static-export": "next build && next export"

Now you can run npm run static-export to create a static export of your blog!

Note: If you run into an issue stating Could not find a declaration file for module 'classnames'., create a file called classnames.d.ts in the @types directory. All you need to add to the file is declare module 'classnames', and that should get the project to build.

Succesfully running the script we just created will result in an out directory containing the static export of your blog being created. While that's great, our aim is a bit further than just a folder on our computer. Also, if you take a quick look at your .gitignore file, you'll see that the out directory is explicitly ignored. This is simply because it's customary to not check build artifacts into source control. But we don't need that!

Publish your blog with Github Actions and Github Pages

Github has this really cool feature called Github Actions. This can help us automate the static code generation whenever we push code to our repository. First, create a new folder in your project called .github. Inside, create a second folder called workflows. Inside the workflows folder, create a file called build-and-deploy.yml with the following contents:

name: Build and Deploy
      - main
    runs-on: ubuntu-latest
      - name: Checkout ğŸ›Žï¸
        uses: actions/[email protected]

      - name: Install and Build 🔧
        run: |
          npm ci
          npm run static-export

      - name: Deploy 🚀
        uses: JamesIves/[email protected]
          branch: gh-pages
          folder: out

We're using the Github Pages Deploy Action written by James Ives to upload the build output (can you spot the mention to the out folder in this snippet?) to a separate branch, which we will use to serve our Github Pages site.

Now we need to do a few things:

Once the Action has completed, you should see a new branch in your repository called gh-pages. We will use this branch for our site:

If you did not use a custom domain, your site should soon be live on https://github-name.github.io. If you already have other Github Pages you can find it at https://github-name.github.io/repository-name.

If you visit your site you'll see there are some issues with the styling... Let's deliver on the final part of the promise and

Fix all issues that come up

This first issue is pretty simple to fix, but let's take a step back to look at what causes this weird absence of styling. If you use "Inspect Element" on your website, and select the Console tab, you'll be greeted with a bunch of error messages and warnings, including Loading failed for the <script> with source <your domain>/_next/static/chunks/commons-5bbe2ea0fe711055318e.js. This is caused by a feature of Jekyll (another tool to create static blogs which is supported out of the box by Github Pages). Directories that start with an underscore are treated differently from other directories, and that is exactly what's happening with the _next directory you see in the path in the error message. Luckily there is a way to disable this behavior. Open the build-and-deploy.yml file we created earlier, and modify the step called Install and Build 🔧 to look like this:

- name: Install and Build 🔧
  run: |
    npm ci
    npm run static-export
    cd out && touch .nojekyll

Once you commit that and wait for the Github Action to complete, your blog should look a lot better!

Note: If you already have a different github pages site, you need to make sure the styles and scripts are served with a base path. You can read more about doing so here. The base path you need to set is the same as your repository name. That page also mentions what you need to do to get images to work.


And that's it! Your blog is now live, for the world to see. Completely driven by your Git repository and some Github Action magic ✨

There are a few other things I ran into while building this blog, like adding tags to articles and making sure dynamically applied TailwindCSS classes do not get stripped out by PurgeCSS, but I won't dive into that now. If you do run into issues, please reach out!