How I Built This Blog (With AI)

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

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.

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.

← Back to all posts