_
Introducing the new blog system with content collections, co-located assets, and optimized images. Here's why it matters and how to use it.
[ SYSTEM_UPGRADE // NEW_FEATURES_DEPLOYED ]
Welcome to the first technical transmission from Anjung Sepi. Today, we’re diving into the infrastructure upgrades that power this blog. If you’re curious about how content collections work or why co-locating assets is a game-changer, this signal is for you.
The Problem: Old School Blogging
Before the upgrade, this blog used a simple file structure:
src/pages/blog/
├── index.astro # Blog listing
└── my-first-post.astro # Individual post
Images lived separately in /public/image/, scattered and disconnected from their posts. This worked for small blogs, but as content grows, it becomes a mess:
- ❌ Orphaned images — Deleted posts leave behind unused images
- ❌ No optimization — Images served as-is, no WebP/AVIF conversion
- ❌ Hard to track — Which images belong to which post?
- ❌ No type safety — Frontmatter was just vibes and prayers
The Solution: Content Collections
Astro’s Content Collections organize blog posts with type-safe frontmatter and co-located assets. Here’s the new structure:
src/content/blog/
└── post-slug/
├── index.mdx # Post content
├── hero.png # Post-specific images
└── screenshots/
└── step-1.png
Key Features
1. Type-Safe Frontmatter
Every blog post now has validated frontmatter defined in src/content/config.ts:
const blog = defineCollection({
type: 'content',
schema: z.object({
title: z.string(),
pubDate: z.string(),
description: z.string(),
tags: z.array(z.string()).optional(),
author: z.string().optional(),
readingTime: z.string().optional(),
draft: z.boolean().optional().default(false),
}),
});
Benefits:
- ✅ TypeScript autocomplete for frontmatter fields
- ✅ Build-time validation (catches typos early)
- ✅ Draft support (
draft: truehides from production)
2. Co-located Assets
Images live alongside their blog post:
src/content/blog/my-post/
├── index.mdx
├── featured-image.png
└── diagrams/
└── architecture.webp
Why this matters:
| Old Way | New Way |
|---|---|
Images in /public/image/ | Images with post |
| Manual cleanup needed | Deleted with post |
| Hard to track usage | Clear ownership |
| No optimization | Automatic optimization |
3. Optimized Images with BlogImage Component
The BlogImage component (src/components/BlogImage.astro) handles image optimization automatically:
---
import BlogImage from '../../../components/BlogImage.astro';
import screenshot from './screenshot.png';
---
<BlogImage
src={screenshot}
alt="A description for accessibility"
caption="Optional caption text"
/>
What happens behind the scenes:
- Format conversion — Generates WebP and AVIF versions
- Responsive srcset — Multiple sizes for different screens
- Lazy loading — Images load only when visible
- Built-in styling — Terminal-themed borders and shadows
How to Create a New Blog Post
Step 1: Create the Folder
src/content/blog/my-awesome-post/
Step 2: Add Content File
src/content/blog/my-awesome-post/index.mdx
Step 3: Write Frontmatter
---
title: "My Awesome Post"
pubDate: "2026-02-22"
description: "A brief description for SEO and social sharing"
tags: ["tutorial", "astro"]
author: "your-name"
readingTime: "5 min"
draft: false
---
Your content starts here...
Step 4: Add Images (Optional)
Place images in the same folder:
src/content/blog/my-awesome-post/
├── index.mdx
└── hero.png
Step 5: Use Images in Content
---
import BlogImage from '../../../components/BlogImage.astro';
import hero from './hero.png';
---
<BlogImage
src={hero}
alt="Hero image for my post"
caption="This is an optional caption"
/>
Real Example: This Blog Post
This very post uses the new system. Here’s how the file structure looks:
src/content/blog/content-collections-guide/
├── index.mdx # This file
└── (future images here)
And in the content, I can import and use images like:
---
import BlogImage from '../../../components/BlogImage.astro';
import diagram from './architecture-diagram.png';
---
<BlogImage
src={diagram}
alt="System architecture diagram"
caption="How content collections work"
/>
Migration Notes
If you’re migrating from the old system:
- Move posts from
src/pages/blog/tosrc/content/blog/ - Convert to MDX — Change
.astroto.mdx - Add frontmatter — Use the schema above
- Move images — Place in post folder
- Update imports — Use
BlogImagecomponent
For Shared Images
Some images are used across multiple posts (logos, icons, etc.). For these, use /public/image/:
<img src="/image/logo.png" alt="Logo" class="my-4" />
This bypasses optimization but works for shared assets.
Technical Deep Dive
How Dynamic Routes Work
The file src/pages/blog/[slug].astro creates dynamic routes:
---
import { getCollection } from 'astro:content';
export async function getStaticPaths() {
const blogPosts = await getCollection('blog');
return blogPosts.map((post) => ({
params: { slug: post.slug },
props: { post },
}));
}
---
This generates:
/blog/first-transmission//blog/content-collections-guide//blog/your-future-post/
Build Process
During build:
- Content sync — Astro reads all
index.mdxfiles - Frontmatter validation — Checks against schema
- Image optimization — Generates WebP/AVIF variants
- Route generation — Creates HTML for each post
- Static output — Ready for deployment
Why This Matters
For Readers
- ✅ Faster page loads (optimized images)
- ✅ Better accessibility (alt text required)
- ✅ Responsive design (works on all devices)
For Writers
- ✅ Type-safe frontmatter (fewer errors)
- ✅ Organized structure (easy to manage)
- ✅ Draft support (work in progress hidden)
- ✅ Automatic image optimization (no manual work)
For Developers
- ✅ Build-time validation (catch issues early)
- ✅ TypeScript integration (autocomplete!)
- ✅ Clean architecture (scalable)
- ✅ Easy maintenance (co-located assets)
What’s Next?
This system sets the foundation for:
- Table of Contents — Auto-generated from headings
- Related Posts — Based on tags/categories
- Search — Full-text search across posts
- RSS Feed — For RSS readers
- Sitemap — Better SEO
Closing Thoughts
Content collections aren’t just a technical upgrade—they’re a workflow improvement. Writing blog posts is now cleaner, safer, and more maintainable.
The initial setup takes a bit of work, but the long-term benefits are worth it. Future-you will thank present-you for making this change.
[ END_OF_TRANSMISSION // QUESTIONS_WELCOME ]
Got questions about the new system? Want to suggest improvements? The signal is open.
> SHOW_RELATED_TRANSMISSIONS
Recommended Transmissions
Based on your current signal, these transmissions may interest you.
First Transmission
Initializing the node. This is the first signal from Anjung Sepi.
Why I'm Trying to Build a Lighter, Greener Website
I recently learned about the internet's environmental impact. Here's what I'm doing about it.