But, Why Do You Need A Blog ?
I recently joined the awesome npmx.dev open source community (if you haven't yet come join us on the Discord!) and proposed to write a blog post on release day to promote the project.
But at that point I didn't have a blog, and I kinda did it on purpose to trick myself to build one before the release!
So I took a few hours (literally less than a day), to build this dead simple portfolio/blog and focused on the essentials: readable, with a light/dark theme and fast.
The interesting part is where the blog posts come from. There's no database, no CMS, no markdown files committed to a git repo. The posts live on AT Protocol (the same decentralized network that powers Bluesky) and the site just reads them at runtime.
Since a blog wouldn't be a blog without a first post, I thought I could write about that, so here's how I implemented it.
The "I don't want to maintain a CMS" problem
I've been through the usual cycle several times. Set up a headless CMS, build the blog around it, abandon everything before posting (OK, maybe the problem was that I wasn't as confident about sharing my views on the internet back then as I am now).
This time I wanted something dead simple so I could focus on actually publishing: write something in a nice editor, hit publish, and have it show up on my site. No redeploy. No git involved. No database to keep alive.
Turns out AT Protocol makes this surprisingly easy.
AT Protocol and WhiteWind
If you're on Bluesky, you already have a personal data repository on AT Protocol. It stores your posts, your profile, your follows, etc. but it can store other things too! The repo is organized into collections, and any app can define its own.
WhiteWind is a blogging platform that does exactly that. It uses a collection called com.whtwnd.blog.entry to store blog posts directly in your AT Protocol repo. You log in with your Bluesky account, write in Markdown, publish, and that's it, sitting in your own data repo, readable by anyone through the public API.
The neat part: since the data is in your repo and not on WhiteWind's servers, any app can fetch and display it. Including, well, my blog 😊
What's Under The Hood
I decided to use Nuxt since it's the framework I know the best, and I didn't want to spend too long setting up this project.
Nuxt is perfect for that, convention over configuration makes it perfect for small projects like this one!
Technically, it's just two routes on the server:
server/
└── api/
├── posts.get.ts # list posts with pagination
└── post/
└── [rkey].get.ts # fetch a single post by key
When you visit the blog page, the server calls com.atproto.repo.listRecords on my Bluesky repo, grabs entries from the com.whtwnd.blog.entry collection, and returns them. No auth needed since everything is public by design.
Each post comes with a visibility level (public, url, or author). In production, only public posts show up. In dev mode, everything is visible so I can preview drafts before publishing them for real.
On the frontend, the Markdown content is rendered with @nuxtjs/mdc (which handles syntax highlighting and all that). The blog index has infinite scroll, each post shows an estimated reading time, and that's about it.
The Writing Workflow
This is my favorite part. When I want to write a post:
- Go to WhiteWind
- Log in with my Bluesky account
- Write in Markdown
- Hit publish
That's it. The post shows up on my site immediately. No commit, no deploy, no webhook. WhiteWind writes to my AT Protocol repo, and the site reads from it on every request.
In dev mode, there's even an edit link on each post that takes me straight back to the WhiteWind editor. Handy for fixing the inevitable typo you only notice after publishing 😁
It Is Also Yours
The whole project is open source and designed to be reusable. The setup is three environment variables:
NUXT_PUBLIC_MODE=production
NUXT_PUBLIC_ATPROTO_SERVICE=https://bsky.social
NUXT_PUBLIC_ATPROTO_REPO=your-handle.bsky.social
Swap in your Bluesky handle, tweak the homepage with your own info, deploy to whatever platform you like (Vercel, Netlify, Cloudflare Pages or anything that runs Nuxt), and you've got a working portfolio with a blog. No database to provision, no server to babysit.
Personally I decided to keep the design as minimal as possible, with Space Mono everywhere and light/dark theme that follows your system preferences.
Why I Like This (Imperfect) Setup
I'm not going to pretend this is the objectively best way to build a blog, it lacks a lot of features and it is limited to only Markdown, so no custom components or interactive experiences (though I have some ideas to circumvent these limitations 👀)
But for my use case it's a sweet spot: I can write from anywhere, no server maintenance, and my posts are also available on WhiteWind!
The other thing I like is that the content actually lives in my AT Protocol repo. If this site disappears tomorrow, the posts are still there. If WhiteWind shuts down, some other editor will come along.
The data is mine in a way that it wouldn't be on Medium or any other platform.
Anyway, if you want a minimal portfolio with a blog that runs on AT Protocol, the code is available on this Github repository.
Fork it and make it yours ❤️