3 min read

Revamping my Blog with NextJS (2020)

Revamping my Blog with NextJS (2020)

Due to the current COVID crisis I had some time to start revamping my blog, focusing on achieving features that were not possible before. All while still keeping the costs to a minimum (or even free). Here I would like to explain how I achieved just this, with the focus on adding the following features:

  • Newsletter subscription serverless
  • Easy previewing of post writing in-browser
  • Easier custom theme control
  • SEO
Note: This post will not go into the details on custom code written, but will rather go into the specific plugins utilized, major problems encountered as well as how certain migration tracks were tackled.

Current Blog Architecture

The current blog system is utilizing Jekyll that is compiling the written markdown pages on each deployment step. Now even though this system has served me well, there is - as in any project - room for improvement. But important is that the new system includes all the features I had before:

  • Markdown Posts
  • Markdown Metadata (also called frontmatter)
  • Code Syntax highlighting
  • Commenting
  • Custom pages (e.g. Custom project pages)

So let's get started!

Used Technology

Over the past few years I have come to love certain technologies that made it swift for me to create scripts, servers and even blog technologies while keeping the new languages I have to learn to a minimum. The following tools are the ones I will utilize for the new Blog system:

  • React.js
  • It is just a heavenly good frontend framework
  • Disadvantage is server rendering / frontend rendering, but there are tools to help us with this.
  • Zeit.co
  • Free for small sites
  • Production deployment to zeit.co is one simple command now --prod
  • Gatsby
  • Easy to use plugins
  • File drivers for "querying" files as a data source
  • Image optimization plugins
  • TailwindCSS
  • Easy to use CSS Framework
  • Allows us to utilize custom CSS through classNames rather than custom css files.
Besides the 2 above, I had to make the decision between Gatsby and Next.js. While both suited my needs, it was easier to work with Gatsby due to the included file drivers required to query my markdown files.

Planned Structure Changes

Currently the blog utilizes the following structure:

  • Pages are under the root / domain
  • Projects are under /projects/:slug
  • Images are all in the /assets folder
  • Dates are in frontmatter configuration as well as file such as YYYY-MM-DD-file-name.md
content/
├── assets
│   └── covers/
│       ├── cover.png
│       └── image.png
└── blog
    └── <category>
        └── YYYY-MM-DD-<post>.md

The above however is definitely not a best practice. Blog posts should have their own specific url category and images should be co-located in the same directory, therefor I chose the following structure for the blog posts:

content/
├── assets
└── blog
    └── <category>
        └── <post>
            ├──  index.md
            └── image.png

This was done mainly manually together with some regex replacements to ensure a smooth process. The folder structure was changed through the following script:

const fs = require('fs').promises;
const path = require('path');

async function walk(dir) {
    let files = await fs.readdir(dir);
    files = await Promise.all(files.map(async file => {
        const filePath = path.join(dir, file);
        const stats = await fs.stat(filePath);
        if (stats.isDirectory()) return walk(filePath);
        else if(stats.isFile()) return filePath;
    }));

    return files.reduce((all, folderContents) => all.concat(folderContents), []);
}

async function start() {
    let articles = await walk('./content/blog-old');
    articles.forEach(async (article) => {
        let re = new RegExp(/([0-9a-zA-Z-\.\\]+)[\\]+([0-9a-zA-Z-\.]+)/g);
        let results = re.exec(article);

        let file = results[2];
        let path = results[1];

        // Create dir
        let newDir = file.replace('.md', '').replace(/[0-9]{4}-[0-9]{2}-[0-9]{2}-/, '');
        await fs.mkdir(`./content/blog/${path}/${newDir}`, { recursive: true });

        // Create file
        let fileContent = await fs.readFile(`./${path}/${file}`);
        await fs.writeFile(`./content/blog/${path}/${newDir}/index.md`, fileContent);
    })
}

start();

Full Gatsby Blog Structure

For the Gatsby set-up I chose the following file structure:

content/
├── static/
├── assets/
├── blog/
│   └── <category>/
│       └── <post>/
│           ├── index.md
│           └── image.png
│   projects/
│     └── <project>/
│         ├── index.md
│         └── image.png
├── config/
│   ├── authors.json
│   └── tags.json
├── src/
├── gatsby-browser.js
├── gatsby-config.js
└── gatsby-node.js

Where gatsby is customizeded through the following files:

  • gatsby-config.js contains the configuration files and modules:
  • gatsby-node.js contains the creation of static pages for:
  • Blog Pagination
  • Blog Pages
  • Tag Pages
  • Project Pages
  • gatsby-browser.js contains the custom css files

CI/CD With GitHub Actions

Next to setting up the blog on Gatsby, I have also decided to utilize GitHub actions for building it. By using this simple YAML script, I could create a .github/workflows/main.yml file that instantly deploys the blog upon push.

name: Deploy Website
on:
  push:
    branches: [ master ]
  pull_request:
    branches: [ master ]
jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - uses: amondnet/now-deployment@v2
        with:
          zeit-token: ${{ secrets.ZEIT_TOKEN }} # Required
          now-args: '--prod' #Optional
          now-org-id: ${{ secrets.ORG_ID}}  #Required
          now-project-id: ${{ secrets.PROJECT_ID}} #Required 
          working-directory: ./