README
Blog Posts
This directory is a placeholder. Your blog posts live elsewhere — see below.
Where posts live
Posts are stored at src/content/data/blog/<slug>.{md,json}.
Each file is self-describing: the filename becomes the slug for routing, and the file
carries a stable id field (format: post-<nanoid>) used by marketing campaigns to
reference a specific post regardless of slug changes.
Example markdown post (src/content/data/blog/my-first-post.md):
---
id: post-abc123
slug: my-first-post
title: 'My First Post'
excerpt: 'A brief description'
featuredImage: 'https://media.gettyimages.com/id/1234567/photo/example.jpg'
publishedAt: '2025-01-15T10:00:00Z'
published: true
---
Full post body in markdown.
Example JSON post (src/content/data/blog/my-first-post.json):
{
"id": "post-abc123",
"slug": "my-first-post",
"title": "My First Post",
"excerpt": "A brief description",
"featuredImage": "https://media.gettyimages.com/id/1234567/photo/example.jpg",
"publishedAt": "2025-01-15T10:00:00Z",
"published": true,
"content": "Full post body."
}
How posts are consumed
Posts are consumed via virtual:content, a Vite virtual module provided by the
airo-content plugin at build time. lib/blog.ts reads content.data.blog — an array of
all posts sorted by filename — and exposes getter utilities (getBlogPosts,
getPostBySlug, etc.) to the rest of the blog UI. (The bare content.blog alias is kept
as a fallback; it is only emitted while no pages/blog.json claims the same name.)
There is no posts.json manifest and no separate glob loader. The plugin discovers
every .md and .json file under src/content/data/blog/ automatically.
Frontmatter fields
Required:
id— stablepost-<nanoid>identifier (stays constant even if slug changes)title— post titleexcerpt— short description (160 chars max recommended)publishedAt— ISO 8601 publication datepublished—truefor live,falsefor draft
Recommended:
featuredImage— image URL (the blog-writing flow supplies a Getty URL). Rendered as the post's card image and hero; a post without it falls back to a blank/placeholder, so the authoring flow treats it as required even though the schema permits its absence.slug— defaults to the filename (my-first-post.md→my-first-post); include only to override.
Optional:
author,updatedAt,category,tagsseoTitle,seoDescription,status(published | scheduled | draft)
Getting started
Ask the AI agent to create posts:
- "Create a blog post about [topic]"
- "Write 5 blog posts about [topics]"
- "Update the blog post about [topic]"
Written by
Wayne Sorbelli
Content creator and writer sharing insights and stories.