Vibe Code to codebykarun.com: The Content Pipeline (Part 4)
Part 4 of 5 • Previously: First Hour of Development
In Part 3, we built the foundation. Now let's make it production-ready with proper caching, optimization, and all the details that matter.
The Initial Implementation
We had basic file loading, but it wasn't optimized. Here's how we refined it:
Initial Prompt:
I need to load MDX files from the content directory, parse frontmatter
metadata, and make the content available to my Next.js pages. The solution
should work with Server Components, use async/await for file operations,
and sort articles by date (newest first). Include proper TypeScript types.
Iteration Prompt:
Can you add error handling for missing files and invalid frontmatter?
Also, I want to fetch a single article by slug, not just all articles.
This gave us robust file loading with proper error handling.
Adding Performance: Caching
The next issue was performance:
Caching Prompt:
These file reads will happen on every request in development. How can I add
caching to improve performance? I'm using Next.js 15's App Router.
The Response: "Next.js 15 caches fetch requests by default, but filesystem operations need manual caching. Let's use unstable_cache
..."
import { unstable_cache } from 'next/cache';
export const getArticles = unstable_cache(
async (category: 'coding' | 'personal'): Promise<Article[]> => {
const contentDir = path.join(process.cwd(), 'content', category);
const files = await fs.readdir(contentDir);
const articles = await Promise.all(
files
.filter(file => file.endsWith('.mdx'))
.map(async (file) => {
const filePath = path.join(contentDir, file);
const fileContent = await fs.readFile(filePath, 'utf8');
const { data, content } = matter(fileContent);
return {
slug: file.replace('.mdx', ''),
title: data.title,
excerpt: data.excerpt,
date: data.date,
tags: data.tags || [],
content,
};
})
);
return articles.sort((a, b) =>
new Date(b.date).getTime() - new Date(a.date).getTime()
);
},
['articles'],
{ revalidate: 3600 } // Cache for 1 hour
);
Result: Instant page loads in development and production.
Building the ArticleLayout Component
Now for the reading experience:
Initial Prompt:
Create an ArticleLayout component that wraps article content. It should
display the title, publication date, estimated reading time, and tags.
Use semantic HTML and make it look professional with Tailwind CSS.
This gave us a basic layout. But I wanted more:
Refinement Prompt:
This works but feels plain. I want better typography with proper hierarchy,
metadata display with icons or separators, and that 'premium blog' feel you
see on sites like Medium. The reading time should be calculated automatically
based on word count (assume 200 words per minute). Use Tailwind's prose
plugin for the article content.
Final Result:
import { formatDate } from '@/app/lib/utils';
import { Article } from '../types/article';
interface ArticleLayoutProps {
article: Article;
children: React.ReactNode;
}
function calculateReadTime(content: string): number {
const wordsPerMinute = 200;
const wordCount = content.trim().split(/\s+/).length;
return Math.ceil(wordCount / wordsPerMinute);
}
export function ArticleLayout({ article, children }: ArticleLayoutProps) {
const readTime = calculateReadTime(article.content);
return (
<article className="mx-auto max-w-3xl">
<header className="mb-8 space-y-4">
<h1 className="text-4xl font-bold tracking-tight lg:text-5xl">
{article.title}
</h1>
<div className="flex flex-wrap items-center gap-4 text-sm text-muted-foreground">
<time dateTime={article.date}>
{formatDate(article.date)}
</time>
<span>•</span>
<span>{readTime} min read</span>
</div>
{article.tags.length > 0 && (
<div className="flex flex-wrap gap-2">
{article.tags.map(tag => (
<span
key={tag}
className="rounded-full bg-muted px-3 py-1 text-xs font-medium"
>
{tag}
</span>
))}
</div>
)}
</header>
<div className="prose prose-neutral dark:prose-invert lg:prose-lg">
{children}
</div>
</article>
);
}
Dynamic Routes with SEO
Each article needs its own URL with proper SEO:
My Prompt:
I need to create dynamic route pages for individual articles. Each article
should have its own URL based on its slug (e.g., /coding/my-article-slug).
Use Next.js 15's App Router with generateStaticParams for static generation.
Include proper SEO metadata generation from the article frontmatter, and
return a 404 page if the article doesn't exist.
The Implementation:
// app/coding/[slug]/page.tsx
import { getArticles, getArticleBySlug } from '@/app/lib/articles';
import { ArticleLayout } from '@/app/components/ArticleLayout';
import { MDXContent } from '@/app/lib/mdx';
import { notFound } from 'next/navigation';
export async function generateStaticParams() {
const articles = await getArticles('coding');
return articles.map(article => ({
slug: article.slug,
}));
}
export async function generateMetadata({ params }: { params: { slug: string } }) {
const article = await getArticleBySlug('coding', params.slug);
if (!article) return {};
return {
title: article.title,
description: article.excerpt,
};
}
export default async function ArticlePage({ params }: { params: { slug: string } }) {
const article = await getArticleBySlug('coding', params.slug);
if (!article) notFound();
return (
<ArticleLayout article={article}>
<MDXContent source={article.content} />
</ArticleLayout>
);
}
What This Got Right:
- Static generation - Fast, SEO-friendly
- Proper 404 handling - Clean error states
- SEO metadata - Automatic from frontmatter
- Type safety - No runtime surprises
The Iterative Process
Notice the pattern:
- Start with basic implementation
- Identify what's missing
- Ask specific questions
- Refine through conversation
- Test and validate
This is vibe coding at its best: iterative refinement through dialogue.
What I Learned
Building this pipeline taught me:
- Next.js 15's caching strategies
- How Server Components optimize performance
- Reading time calculations
- SEO metadata generation
- Error handling patterns
I didn't just get code—I leveled up as a developer.
Coming Up Next
In Part 5, we'll tackle the design system and theming—how to create a beautiful, consistent look without being a designer.
Next in the Series
👉 Continue to Part 5: Design System & Theming
Learn how I created a professional design with dark mode support using AI guidance.
More in this series: Part 1: What is Vibe Coding? • Part 2: Tech Stack • Part 3: First Hour • Part 5: Design System • Part 6: Lessons Learned • Part 7: Best Practices