Why am I starting a blog in 2026?
Building your own blog from scratch
I’ve been toying with the idea of starting my own blog for years now, with various attempts of setting up one of the many static site generators that ended up collecting dust, never to be filled with content. Something wasn’t clicking, I have a hard time adopting something if I don’t know it from the inside out, as I have a habit of diving head first in new topics, I needed something to learn, something to build.
A few weeks ago, given the free time I’ve been having lately, I started browsing Hacker News to stay up-to-date once again. There the inspiration started building up again, I was seeing so many people publishing articles from their personal blogs, they were neat, personal, and custom-built. “This is how a tech blog should be!”.
And so the rabbit hole began. I’m a firm believer that reinventing the wheel is a crucial step in learning, to understand something deeply one must have built it at least once.
I googled left and right, read articles and tutorials to get an idea of what direction I should take; I was going to build my blog into my website. There are some insights I gathered:
- If I wanted to have any kind of SEO my single page website wasn’t going to cut it, I’d need some level of SSR to allow bots and crawlers to discover my articles.
- MDX was the way to go for the content itself, it’s an extension of Markdown that allows JSX inside the content, any React component can be imported and included in the middle of an article.
- One can go far in terms of making a blog pretty and feature rich, I needed to be selective with the base components I’d use in my articles without implementing everything.
Chosen stack
This site was originally strung together by me to have a public landing page for my professional persona, built over a period of free time while trying to learn React, with lost of copy-pasting left and right to get something running, I worried more about how it looked rather than how it worked.
Luckily, I have been working recently on a new web app built with Next.js, which I’ve been familiarizing myself with. It seemed to tick most of the boxes mentioned above: SSR with good SEO support and native support of MDX through @next/mdx. I just needed to migrate my site to it.
The migration
The “migration” was mostly about re-deriving the app following the structure of Next.js. I had to map the SPA react-router-dom routes to the App Router’s file-system routing and decide what became Server Components, which allows them to be rendered completely statically, versus what had to stay a Client Component (to allow user interactivity and theming), which just means adding a 'use client' at the top of the file.
For assets not much was needed, mostly switching from using the /public folder for almost everything, to having assets part of src/assets and making use of next/image to enjoy a couple of optimizations, such as better lazy loading while maintaining layout stability while the images haven’t yet loaded.
Next.js images also come with built-in optimization, which automatically serves correctly sized images for each device, resizing them on demand. Unfortunately I had to opt out from this option, being that this site is hosted on GitHub Pages, I had to configure my project to output: 'export', which generates a static HTML file for each page, meaning that no Next.js server would be running to serve as the backend.
Building the blog
Once the base was set, it was time to move to the interesting part, setting up page rendering from an .mdx file, and building the Lego pieces that would end up composing my articles (like this one).
The rendering part is pretty standard, there’s a dynamic route at /blog/[slug] that resolves a static parameter (slug), which is used to locate the corresponding .mdx file on disk at build time. That file is compiled by @next/mdx into a React component during the Next.js build. The result is static HTML generated ahead of time, with client components only where explicitly embedded, which keeps the blog fast and crawlable.
Under the hood, that MDX compilation step is driven by a pipeline: remark operates on the markdown AST, while rehype takes over once the content has been transformed into HTML. Plugins I’m using for them range from simple ones such as remark-mdx-frontmatter, allowing YAML metadata at the top of the post in this shape:
---
title: 'Post title'
description: 'A description.'
date: '2024-11-08'
tags: ['some', 'tags']
related: ['related-post-slug']
---allowing the post to be discovered, classified and listed on the index page; or remark-gfm (for GitHub Flavoured Markdown), which adds support for all extensions provided by GitHub (tables, task lists, footnotes, …).
And here comes the part that was fun for me, defining the components that would allow my posts to come to life.
Code
Since this was going to be at least partly a tech blog, a non-negotiable was going to be inline code and code blocks. I’m using rehype-pretty-code with Shiki 式 for syntax highlighting, which supports VS Code themes. On top of coloring standard code blocks, println!("{msg}") it allows also to apply the same styling to inline code.
use std::collections::HashMap;
#[derive(Debug, Clone, Copy)]
struct Point<T> { x: T, y: T }
impl<T: Copy + std::ops::Add<Output = T>> std::ops::Add for Point<T> {
type Output = Self;
fn add(self, rhs: Self) -> Self {
Self { x: self.x + rhs.x, y: self.y + rhs.y }
}
}
fn demo(n: i32) -> Result<(), Box<dyn std::error::Error>> {
let p = Point { x: 1, y: 2 } + Point { x: 3, y: 4 };
let msg = match n { x if x > 0 => "pos", 0 => "zero", _ => "neg" };
let mut map: HashMap<String, i32> = HashMap::new();
map.insert(format!("{p:?}"), n);
println!("{msg}: {map:?}");
Ok(())
}Asides
For mentions and additional information that break the flow of the main article content, I wanted to have something inspired by Atlassian’s info panels, which I have grown quite accostumed to as a Jira & Confluence user, people that have worked with me will know that I’ve had my phase where I’d compose my user stories on Jira using almost exclusively these blocks, I have yet to understand if I’ve grown out of them.
They are implemented using the proposed directives syntax defined by CommonMark, and look code blocks using : instead of `.
:::info
content
:::
:::warning{title="Optional title"}
content
:::
:::error
content
:::This generic form turned out quite useful for the vast majority of custom components I’ve built for the blog.
Inline notes
You might have seen a couple of these in the article, it’s my approach to footnotes that don’t break the reading flow. The blue ✱ indicates presence of a note which can be viewed by clicking on the corresponding word. They also make use of directives, this time in inline form :note[word | Extra context in a popover.], more akin to an image or link directive.
Images and captions
For images, I only wanted to keep a cohesive layout, so pictures are styled like code blocks and asides, with a fixed width. Caption can be placed pretty much anywhere, they don’t need to be paired with a figure, although this makes me lose a bit of semantic HTML, and use yet another directive that looks like this: ::caption[formatted **text**].
Cursive
One final touch I ended up adding, and I wonder if I’ll ever justify its existence, is cursive text, to convey sarcasm, hyperboles, and whatever fits my creative flow in the moment. They are simply formatted like %this%.
What now?
As I approach the end of this first article, and prepare to publish it, I can’t avoid to wonder: “Will someone ever read this?”; is this going to be the sole attempt at running a blog, or will I keep going and write more, and refine the art.
Only time will tell, all I have let to do is to git push.