Time goes by fast, the last time I posted on this site was in 2022. I’m alive and doing well, more than well to be honest, a lot of good news1.
But today let’s talk about the state of this website. In the last week there has been a considerable change to the makeup and hosting of this website. It wen’t from being a Next.js website hosted on Netlify to being a Hugo SSG(static site generator) on a Debian VPS on Linode. This site has always been an SSG, even with Next.JS it was using the static html export feature.
Why am I doing this?
Before getting into specifics, looking back there has been a lot of changes in the way I approach these problems. I must accept my naiveté and short sighted when setting my website four years back.
I’m looking for simplicity and having control of my website in this new implementation.
- I’m tired of keeping the dependencies up to date of a JavaScript project2.
- The website was one of my first Next.JS projects therefore it has some questionable code and questionable DX.
Migrating to Hugo
Hugo is a static site generator written in Go, I’ve never been interested in learning Go have always been aware of all the tools that I used daily that are written and maintained with it. Also a year back I took up a new job which uses Go as the primary backend language, but I digress.
There is no customisation here, I downloaded a copy of Hugo and chucked in my content and bootstrapped a custom theme using simple.css3.
I was able to hit the ground running fairly quickly by dropping my markdown files with YAML frontmatter.
title: RSS Feed for next.js blog
description: >-
On creating an RSS feed for this blog, and how it's not as tedious as you think.
date: '2021-06-19T17:12:26.723Z'
thumbnail: 'cmb_np.jpg'
category:
- projects
- web-dev
tags:
- nextjs
type: post
slug: rss_feed_nextjs
Configuring multiple taxonomies based on the attributes was straightforward.
[taxonomies]
category = 'category'
tag = 'tags'
But one of my biggest pain points was asset management, before there was a singular folder which had all of my images and it was a mess. I wanted to switch over to use page resources instead, which will have all the assets needed on the content folder itself.
With my laziness, coaxing chat-gpt and spending more time bug fixing resulted in this script, which migrated all my blog posts to their own folders with relative links to images.
import os
import shutil
import re
def migrate_markdown(source_dir, destination_dir, base_img_path):
# Ensure destination directory exists
if not os.path.exists(destination_dir):
os.makedirs(destination_dir)
# Iterate through Markdown files in source directory
for filename in os.listdir(source_dir):
if filename.endswith(".md"):
# Read Markdown file
with open(os.path.join(source_dir, filename), "r") as file:
markdown_content = file.read()
# Extract folder name
folder_name = os.path.splitext(filename)[0]
# Create folder
new_folder_path = os.path.join(destination_dir, folder_name)
os.makedirs(new_folder_path)
# Move Markdown file
new_markdown_path = os.path.join(new_folder_path, "index.md")
shutil.copy(os.path.join(source_dir, filename), new_markdown_path)
# Copy images to folder
img_paths = re.findall(r"/img/([\w-]+.\w{1,5})", markdown_content)
for img_path in img_paths:
img_filename = os.path.basename(img_path)
shutil.copy(os.path.join(base_img_path, img_path), os.path.join(new_folder_path, img_filename))
# Update image paths to be relative
markdown_content = re.sub(r"/img/([^'\"\(\)]+)", r"\1", markdown_content)
# Write updated Markdown content
with open(new_markdown_path, "w") as new_file:
new_file.write(markdown_content)
# Example usage
source_dir = "./posts"
destination_dir = "./content/posts"
base_img_path = "./static/img" # Base path for images
migrate_markdown(source_dir, destination_dir, base_img_path)
Tips I wish I’d knew days back
Yes to RTFM, but these are the customisations that were done.
Customize markdown image rendering
It’s best that the image tags in markdown be rendered in a figure
tag and have compression applied you can do that using an hook in the layouts directory. layouts/_default/_markup/render-image.html
.
{{- $imgURL := .Destination -}}
{{- $altText := .Text -}}
{{ $image := .Page.Resources.Get $imgURL }}
<figure>
{{ if $image }}
{{ $image = $image.Process "resize 1000x webp q50" }}
<img src="{{ $image.RelPermalink }}" class="summary-image">
{{ end }}
<figcaption>{{ $altText }}</figcaption>
</figure>
This also has image processing, where I requested that the images be resized to 1000px on width and exported as webp
with 50% quality.
Minifying CSS
I found the documentation on this, but what I missed was the fact that the css files should be in the assets
directory not the static
directory.
{{ $style := resources.Get "style.css" | minify | fingerprint }}
{{ $custom := resources.Get "custom.css" | minify | fingerprint }}
<link rel="stylesheet" href="{{ $style.Permalink }}">
<link rel="stylesheet" href="{{ $custom.Permalink }}">
Minifying HTML content
I’d guessed this is done via configuration, but it’s done using during the build stage using a cli flag
hugo --minify
Conclusion
Migrating to hugo only took me a couple of days, and compared to the custom Next.js code base, this is much better DX and updating my toolchain is only a single step to replace the hugo binary. This is one of the reasons not choosing to opt into using Tailwind as well.