How I built this blog
Why I Built My Own Blog Instead of Using Medium
I've been writing drafts in Notion for almost two years.
Half of them never got published.
Not because I didn’t want to write them, but because every time I thought about moving them somewhere public like Medium, Hashnode, Substack, or a custom site, the setup friction killed the momentum.
So one Saturday afternoon, I stopped looking for the "right" platform and built my own.
This is the story of that weekend: the stack, the editor, the schema, the deployment bug that took the site down forty minutes after launch, and all the tiny decisions that make a blog feel like it actually belongs to you.
The problem with writing on someone else’s platform
Notion is an amazing place to write.
It’s also trapped behind someone else’s product decisions. Their domain, their search, their analytics, their export quirks, and their pricing roadmap.
Medium solves distribution but makes your writing feel rented. Hashnode is more developer-friendly, but it still doesn’t feel fully yours. Ghost came closest, but paying every month for something I could build in a weekend felt unnecessary.
The real reason was simpler, and honestly a little vain.
I wanted the blog to feel like the rest of my site.
Same typography. Same dark theme. Same hover animations. Same spacing system.
That’s hard to achieve when your writing lives at:
someone-else.com/@you
So I opened a new Next.js route, created a fresh Supabase project, and gave myself two days.
A stack optimized for finishing
The portfolio already ran on Next.js 16, so technically the blog wasn’t even a new app.
It was just four routes I hadn’t built yet:
/blogs/blog/[slug]/admin/admin/login
The real question was where the content should live.
For the database, I picked Supabase mostly because it removes the entire backend setup phase from small projects.
You get Postgres, authentication, storage, row-level security, and a generous free tier without touching infrastructure.
For writing, I used Tiptap.
Most editor discussions turn into a fight between Markdown purists and people who just want Cmd+B to work properly.
Tiptap sits somewhere in the middle. It’s headless, ProseMirror-based, and flexible enough to feel custom without forcing you to reinvent editing from scratch.
It feels a lot like Notion’s editor, except this one belongs to me.
For syntax highlighting, I used Shiki.
That decision was mostly aesthetic.
Since Shiki runs at build time, the browser ships almost zero JavaScript for code highlighting. The output is just clean HTML with inline styles using the same tokenizer VS Code uses internally.
The stack ended up being surprisingly small:
Next.js 16
Supabase
Tiptap
Shiki
sanitize-html
That was enough to ship a complete writing platform over a weekend.
The database ended up being simpler than expected
The entire blog runs on a single posts table.
create table posts (
id uuid primary key default gen_random_uuid(),
slug text not null unique,
title text not null,
excerpt text,
cover text,
tags text[] not null default '{}',
content text not null default '',
reading_time integer not null default 1,
status text not null default 'draft'
check (status in ('draft', 'published')),
published_at timestamptz,
author_id uuid references auth.users(id) on delete set null,
created_at timestamptz not null default now(),
updated_at timestamptz not null default now()
);