Three weekends to build my blog a static generator
Jul 11, 2026 · ≈ 13 min read
Here's how it happened: my blog needed rebuilding. After the GCP ban I decided never to depend on a backend again. But hand-writing HTML to maintain dozens of posts obviously wasn't realistic. I needed a tool.
The existing options were all too heavy — Hugo, Jekyll, Astro, Next.js. They're good tools, but I didn't want to pull in 200MB of node_modules for a blog. I needed exactly three things: turn Markdown into HTML, wrap it in a template, generate RSS.
So over three weekends, I wrote a Node script in under 200 lines.
Weekend one: figuring out what NOT to build
The problem with most tools isn't too few features — it's too many. I spent the first weekend writing a "not needed" list:
- No frontend framework — plain HTML + CSS is enough
- No hot reload — run the script manually after saving a file
- No plugin system — there's exactly one user, and it's me
- No CMS — the file system is my CMS
- No image optimization — Cloudflare's free CDN will handle it
This process of subtraction mattered more than writing code. It forced me to see what a blog actually needs: content + typography + links. Everything else is noise.
Weekend two: writing the code
The core logic was simple:
const fs = require('fs');
const path = require('path');
const { marked } = require('marked');
// Read all Markdown files
const posts = fs.readdirSync('content')
.filter(f => f.endsWith('.md'))
.map(f => parsePost(f))
.sort((a, b) => b.date - a.date);
// Generate each post's HTML
for (const post of posts) {
const html = applyTemplate(post);
fs.writeFileSync(`dist/${post.slug}.html`, html);
}
// Generate the index page
const indexHTML = applyIndexTemplate(posts);
fs.writeFileSync('dist/index.html', indexHTML);
// Generate RSS
const rss = generateRSS(posts);
fs.writeFileSync('dist/rss.xml', rss);
The real code was a bit longer — frontmatter parsing, URL normalization, relative paths, syntax highlighting. But the core idea was those three steps: read, transform, output.
Weekend three: deleting it all and rewriting
After weekend two, I tried importing 20 old posts. Several problems appeared:
- The template was too rigid — every post got an identical layout, no room for special typography
- Slow generation — only 20 posts, yet every run regenerated every file
- The frontmatter parser wasn't robust; special characters crashed it
On the third weekend I deleted the whole script and rewrote it. Simpler this time — I gave up on automatic Markdown conversion and hand-wrote the HTML instead. It sounds like a step backwards, but in practice:
- The HTML file IS the source file — no "build" step needed
- WYSIWYG — finish writing, refresh the browser
- Total control over every line of markup
- No dependencies, no build step, no
node_modules
In the end this "static generator" degraded into a 50-line script that does exactly one thing: scan the HTML files under posts/, extract titles and dates, and generate the list on the homepage.
Less is actually more
This experience made me rethink what "tools" mean. The best tool isn't the one with the most features — it's the one that's just enough. For a personal blog, a text editor and an HTML file are the best CMS there is.
If you want to start blogging too, my advice: write first, think about tools later. Get the content out, put it in an HTML file, upload it anywhere that hosts static files. When hand-maintaining becomes painful, then consider automation. But most of the time, the pain of maintaining things by hand is far smaller than the pain of learning a complex framework.
This site's source can be found on GitHub. No build step — clone it and it just runs.