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:

  1. Human simplicity - Add a .md file, push, done
  2. 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 logo
  • AppFooter.vue - Copyright footer
  • PostCard.vue - Post preview for listings
  • SocialShare.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:

  1. Create my-post.md with frontmatter
  2. Push to git
  3. 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

  1. AI is a force multiplier, not a replacement. It wrote 90% of the code. I made 100% of the decisions.

  2. Structure enables speed. Clear conventions meant less back-and-forth, feIr mistakes.

  3. Types are documentation. The Post interface tells the AI exactly what a post looks like. No ambiguity.

  4. 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.

← Back to all posts