How I Built This Blog (With AI)
26 January 2026
There’s a lot of open-source, even closed source blog platforms out there. But why choose one of those, when I can just vibe-code my own. If you are like me, and learn by doing, why not just test new tech by making stupid things. I’m not a programmer, but I know that for something as small as this, vibe coding is just efficient.
In the end, I just wanted somewhere I could create a markdown page, and it would magically be a blog.
The Stack Decision
I landed on:
- Vue 3 - Component-based, great DX
- TypeScript - Types as guardrails for AI-generated code
- Vite - Fast dev server, modern tooling
- vite-ssg - Static site generation for SEO
- vite-plugin-pages - File-based routing (no config)
- unplugin-vue-markdown - Write posts in Markdown with Vue components inside
Two goals: human simplicity (add a .md file, push, done) and AI maintainability (strong types, clear file structure, fast feedback).
The Migration Process
Step 1: Scaffold the Project
The AI created the Vite + Vue 3 + TypeScript project structure:
src/
├── components/ # Reusable pieces
├── composables/ # Shared logic
├── layouts/ # Page wrappers
├── pages/ # File-based routes
└── types/ # TypeScript interfaces
Every file has one job. An AI (or human) can find anything in seconds.
Step 2: Extract Components
The monolithic HTML became discrete components:
AppHeader.vue- Sticky nav with logoAppFooter.vue- Copyright footerPostCard.vue- Post preview for listingsSocialShare.vue- Share buttons (Twitter, Facebook, LinkedIn, copy link)
Each component is under 100 lines. Self-contained. Testable in isolation.
Step 3: Build the Post System
I wanted:
- Posts as Markdown files with YAML frontmatter
- Auto-discovery (no manual index to update)
- Vue components usable inside Markdown
The solution: import.meta.glob scans src/pages/posts/*.md at build time:
const postModules = import.meta.glob<MarkdownModule>(
'../pages/posts/*.md',
{ eager: true }
)
Each post exports its frontmatter as named exports. The usePosts() composable collects them, sorts by date, and returns a typed array. Adding a post is literally:
- Create
my-post.mdwith frontmatter - Push to git
- Cloudflare builds and deploys
No config files to edit. No index to maintain. The filesystem is the database.
Step 4: Add the Archive
The home page shows latest posts. But I also wanted a date-based archive. Instead of cramming it into a sidebar (which felt cluttered), I made it a separate page:
/- Latest posts/archive- Posts grouped by year and month
Navigation tabs in the header. Clean separation of concerns.
Step 5: Create the Template
To make future posts easy, I added _template.md — a skeleton post with all the frontmatter and boilerplate. The underscore prefix excludes it from the post listing. Copy, rename, write.
Cloudflare Builds
In Cloudflare, a simple worker runs npm build on code changes. I already use Cloudflare for other things so it made sense, but GitHub Pages would work just as well.
The Result
A blog that builds in seconds, deploys automatically on push, and requires zero config to add posts. Total time from static HTML to production Vue app: 30 minutes.