Vibe Code to codebykarun.com: The First 60 Minutes (Part 3)
Part 3 of 5 • Previously: Choosing the Tech Stack
In Part 2, we chose our tech stack through informed conversation. Now comes the exciting part: actually building something.
Project Initialization
My Setup Prompt:
Let's initialize a new Next.js 15 project with TypeScript. I want to use
Tailwind CSS for styling. Set up the project with a clean directory structure
that separates content, components, utilities, and types. Use path aliases
(@/app, @/components, etc.) to make imports cleaner. Also, I prefer Biome
over ESLint for linting—it's faster.
Within minutes, I had:
- Fresh Next.js 15 project
- TypeScript configured with strict mode
- Tailwind CSS installed and configured
- Biome set up for linting
- Path aliases in
tsconfig.json
Building the Directory Structure
The AI suggested this structure, and we refined it together:
codebykarun-site/
├── app/
│ ├── components/
│ ├── lib/
│ ├── types/
│ ├── coding/
│ ├── personal/
│ └── layout.tsx
├── content/
│ ├── coding/
│ └── personal/
└── public/
Why this structure?
app/components
- Reusable UI componentsapp/lib
- Utility functions and helpersapp/types
- TypeScript type definitionsapp/coding
&app/personal
- Route handlers for each sectioncontent/
- MDX blog posts separated by category
The Content Architecture
Content Architecture Prompt:
I need utility functions to read MDX files from the content directory, parse
frontmatter metadata (title, excerpt, date, tags), and return typed Article
objects. The functions should support two categories: 'coding' and 'personal'.
Make sure everything works with Next.js 15's Server Components and includes
proper error handling.
This gave us the foundation for the entire content system:
// app/types/article.ts
export interface Article {
slug: string;
title: string;
excerpt: string;
date: string;
tags: string[];
content: string;
}
// app/lib/articles.ts
import fs from 'fs/promises';
import path from 'path';
import matter from 'gray-matter';
import { Article } from '@/app/types/article';
export async function getArticles(
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()
);
}
The Magic of Contextual Problem Solving
My first attempt at MDX rendering hit a snag:
Me: "The MDX content isn't styling properly. Code blocks look broken."
Instead of diving into documentation, I just described the problem. Within seconds:
import { MDXRemote } from 'next-mdx-remote/rsc';
import { highlight } from 'sugar-high';
const components = {
code: ({ children, ...props }: any) => {
const codeHTML = highlight(children);
return <code dangerouslySetInnerHTML={{ __html: codeHTML }} {...props} />;
},
};
export async function MDXContent({ source }: { source: string }) {
return <MDXRemote source={source} components={components} />;
}
Problem solved. The AI understood the context, knew about syntax highlighting libraries, and implemented a solution that worked with Next.js 15's Server Components.
Component Architecture
We established a pattern for components:
// app/components/ArticleCard.tsx
interface ArticleCardProps {
article: Article;
variant?: 'default' | 'featured';
}
export function ArticleCard({
article,
variant = 'default'
}: ArticleCardProps) {
const isFeatured = variant === 'featured';
return (
<article className={/* ... */}>
{/* ... */}
</article>
);
}
Each component:
- Has a single responsibility
- Is properly typed
- Is reusable
- Follows consistent patterns
What We Accomplished in Hour One
By the end of the first hour:
- ✅ Project initialized with modern tooling
- ✅ Clean directory structure
- ✅ Type-safe content loading system
- ✅ MDX processing with syntax highlighting
- ✅ Basic component architecture
- ✅ Everything working with Server Components
No tutorial following. No Stack Overflow diving. Just conversation and iteration.
The Key Realization
What hit me during this first hour: I wasn't just getting code written faster—I was learning as I went.
Every prompt response included context:
- Why this pattern?
- How does this work with Next.js 15?
- What are the alternatives?
I wasn't blindly copying code. I was understanding it.
Coming Up Next
In Part 4, we'll dive deep into building the complete MDX processing pipeline with caching, optimization, and all the production-ready details.
Next in the Series
👉 Continue to Part 4: Building the MDX Pipeline
Learn how we built a production-ready content system with proper caching and error handling.
More in this series: Part 1: What is Vibe Coding? • Part 2: Tech Stack • Part 4: MDX Pipeline • Part 5: Design System • Part 6: Lessons Learned • Part 7: Best Practices