Rebuilding my blog from scratch with Next.js
April 23, 2022Update (4/27/2024): I have since rebuilt my blog (once again), and the tags links will no longer work. I may write another post on the rewrite, but maybe not.
Version 0.1.x
The first iteration of my blog was built using a pre-configured GatsbyJS site template. I chose a template (Novela by Narative) that checked all my boxes for an easy tool to start sharing my writing.
- The theme was modern and sleek
- Everything was mobile-friendly
- Lots of plug-and-play options and prebuilt components that flowed together
- Article metadata that connects to an author bio page
- Reading time estimates and progress bars
- Easy image optimizations
- A config for a Google Analytics tag
- Deployment was straightforward with GitHub pages
But most importantly, all I had to do was add Markdown pages to a directory to get started. At the time, I didn't really know much about GatsbyJS, other than it being a popular static site generator, and my modern JavaScript knowledge was introductory at-best (albeit growing each and every day). So this seemed like the perfect route forward.
The problem
As I started writing more and more posts, I began noting different features I wanted to include on my site. These weren't terribly complex additions, but rather simple things like adding tags to group common posts or wanting to change the styling of the inline code snippets.
I soon discovered that trying to color outside the lines when using a cookiecutter template can get very complex, very quickly.
I found myself digging into nested modules in my node_modules
folder where I would need to overload different functions and components to get what I wanted. It was added complexity than I didn't foresee when I signed up to use a template. Plus, this site could eventually house more than just a blog, and the template I chose wasn't designed to accommodate much else.
What now?
If you haven't noticed from the timestamp on my last article, I haven't written a post in ~9 months. This is largely because I was interviewing for new positions and eventually joined the team at Radar as a Product Engineer! Preparation for interviews and taking a break after accepting this new position occupied much of the time I would normally spend writing. Plus, taking a break from coding outside of work is a good way to avoid burnout 🤷♂️.
The reason I mention this is because one of the technologies we use at Radar is Next.js. Next.js was something I was already trying to learn in my free time, but I've been able to gain a good amount of experience with it since I've started my new role, and my modern JavaScript fundamentals and understanding have grown drastically in the past several month (expect a lot more JavaScript posts going forward).
Seeing the power of Next.js and knowing I wanted my blog to be more flexible, I decided to recreate my blog from scratch using Next.js.
Next.js
A quick prelude on Next.js for those who have never used it. Vercel, the creators of Next.js (and a company to keep 👀 on) says it best, Next.js is...
"The React Framework for Production"
Next.js comes with a ton of great built-in features including:
- Static + server-side rendering
- Smart bundling and code-splitting, TypeScript support, Routing, Fast Refresh, CSS + Sass support all without complex configs
- Image optimization
- Intuitive code organization (because there are a million-plus ways to organize a React project)
- Static HTML exports (this one is important for our purposes)
- So much more...
It's an awesome framework.
Coincidentally enough, the Next.js hands-on tutorial walks you through setting up a blog with Next.js. Following this tutorial will set you up with a great and simplistic starter blog (but not quite ready for deployment on GitHub pages, where this blog lives at the time of writing).
A few things this tutorial covers:
- Creating the Next.js app
- Basics like in-app navigation, styling, adding images and more
- Setting up metadata, blog pages, and pre-rendering
- Markdown ➡️ HTML
- Reworking your app to use dynamic routing
The tutorial is great so I won't cover anything that's already covered there. If you're interested in using Next.js, you should definitely go through it.
Beyond the basics
As mentioned, the Next.js tutorial is a great starting point, but I wanted to get my blog to a place where it was comparable to the previous iteration, along with the new features that encouraged me to take on this project in the first place.
To keep things concise, we'll cover adding tags, adding a custom domain from Google Domains to GitHub pages, and adding a GitHub action to automatically build and deploy our blog on each commit.
Tags
Organization within code and outside of code is always at the top of my priority list, so categorizing posts by tag was first on my blog todo list.
Adding tags to each post
First, let's add tags to each of our posts. We can work off of the blog data section of the Next.js tutorial and add some additional YAML metadata to our markdown posts using gray-matter.
Within the current metadata, add a list of tags relevant to the post:
I won't cover rendering of the tags below each posts in the post list, but the tags list should get picked up automatically with the getSortedPostsData()
function that was already written and is called by getStaticProps()
in order to pass props down the the post related components.
/tags/[tag]
What we've done so far will associate posts with a list of tags, but now we also want a page for each tag that lists out the associated posts. For example, to view python
related posts, we can go to /tags/python.
With Next.js, this can be done easily using dynamic routes.
Under the pages/
directory, create a pages/tags/[tag].js
file structure. We'll be repeating similar patterns done within pages/posts/[id].js
.
Like earlier, we need to implement getStaticProps()
(more info here), so we can render these pages at build time. We also need to implement getStaticPaths()
(more info here) to get a list of all possible tags at build time.
Let's create a lib/tags.js
file to house some of the helper functions to implement getStaticProps()
and getStaticPaths()
.
First, we want to get a list of all the tags so we can write getStaticPaths()
. This will require processing all of the files within posts/
, and processing the metadata using gray-matter.
Now we can call this within pages/tags/[tag].js
under getStaticPaths()
Now, we can add another helper function getPostDataByTag()
in lib/tags.js
to fulfill getStaticProps()
. This is basically what we already do with pages/posts/[id].js
, and it's not very efficient to do this twice, but all of this is happening at build time so it's not a huge deal for us.
For this, we will use remark to process our markdown files.
We can call this helper function in pages/tags/[tag].js
under getStaticProps()
Now, you can render the associated tag pages as you wish, but I did it with a few components I had set up:
All tags
The last thing we want to add is a page containing all of the tags in a list, and all of the posts organized into categories.
To do this, we can add pages/tags.js
, which can be reached at /tags.
Once again, we want to implement getStaticProps()
so we can pre-render this page at build time. Luckily, we can reuse the two functions we wrote in lib/tags.js
to make this easy.
Now, you can render this as you'd like using tagsWithPosts
as a prop in your page component. I also like having a toggle to show/hide the associated posts.
Now we have a page with all of our tags 🙌.
Adding a custom domain
Next on my todo list was adding a custom domain to transition from bschoeneweis.github.io ➡️ bradleyschoeneweis.com. GitHub gives a good overview of the steps here, but we'll go through it below specifically using Google domains.
We'll be using an apex domain (e.g. bradleyschoeneweis.com).
GitHub changes
- Go to the repository settings for your GitHub pages repository
- Click into the Pages section
- Type your apex domain into the Custom domain input and click Save
Google domains changes
- Go to your Google domains registrar
- Click Manage on the domain you'd like to use
- Navigate to the DNS settings and under Resource records, click Manage custom records
- Refer to the GitHub documentation for the official IP address list, but add the following
A
andCNAME
records
Final steps and confirmation
- Head back to the Pages settings for your repository and check Enforce HTTPS
- After a bit of time has passed, run
dig www.mynewdomain.com +nostats +nocomments +nocmd
replace with your domain name and your output should look similar to the following:
Now your custom domain should be all set up with your GitHub pages blog! 🎉
Simple automated deployment
For the final touches, I didn't want to have to worry about building the blog locally each time, so we'll create a simple GitHub action to build and serve our blog on each commit.
The package.json
scripts
If you don't have one already, you should create a build
script in package.json
as build: "next build"
.
I won't cover it here, but you'll also need to go through tutorials related to exporting your Next.js project as static HTML. There are a few gotchas here. For example, you cannot use the Next.js image optimization or API routes. You can see the full list of unsupported features.
Long story short, within your package.json
file, you should add an export: "next export"
script. This will create an out/
directory with HTML files when this command is run. You probably want to add this directory to your .gitignore
file.
Deploy key
We will be using peaceiris/actions-gh-pages@v3 for making the deployment step easy. As a part of this, we want to set up an SSH deploy key for safety.
You can use an SSH key that you already have setup, or create a new one with:
- Navigate back to your repository settings, and under security, go to Deploy keys
- Click Add a deploy key and enter
ACTIONS_DEPLOY_KEY
as the title - Paste in your public RSA key (ends with
.pub
) - Check Allow write access and then click Add key
deploy.yml
Now that every thing is set up, we can put this all into a GitHub actions file.
From the root of the project, create .github/workflows/deploy.yml
and refer to the following:
A few things to note:
- The branch I actively work on is
develop
, so you'll want to update that to the branch you push to - If you didn't name your deploy key
ACTIONS_DEPLOY_KEY
, change thedeploy_key
value tosecrets.[Your key name]
in the Deploy step - The
cname
value in the Deploy step should be replaced with your newly configured custom domain
Now, each time you commit and push up you blog changes, your blog should automatically be built and served under your custom domain.
Just like that, your blog is just as functional as a cookiecutter template 🍪 and more easy to build upon than ever before.