How I Built This Blog (With AI)
There’s a lot of open-source, even closed source blog platforms out there. But why chose 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 s amll 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
Why this stack? It optimizes for two things:
- Human simplicity - Add a
.mdfile, push, done - 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
No magic. 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
To remind the reader, 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.
4. Cloudflare Builds
In Cloudflare, we have a simple worker that runs npm build, this triggers on code changes. Very simple to set up, and while i could have used github pages, i already use cloudflare for a lot of other things, so it made sense for me.
The Result
A blog that:
- Builds in seconds
- Deploys automatically on push
- Requires zero config to add posts
- Has type-safe, component-based architecture
- Is documented for both humans and AI
Total time from static HTML to production Vue app: 30 Minutes.
Lessons Learned
AI is a force multiplier, not a replacement. It wrote 90% of the code. I made 100% of the decisions.
Structure enables speed. Clear conventions meant less back-and-forth, feIr mistakes.
Types are documentation. The
Postinterface tells the AI exactly what a post looks like. No ambiguity.The filesystem is underrated. File-based routing and glob-based discovery beat config files every time.
If you’re building with AI assistance, invest in structure upfront. It pays dividends every time the AI touches your code.