Personal website and blog built with Next.js 15, featuring MDX-based content and modern design.
This project uses a clean, purpose-driven component organization:
src/
├── components/
│ ├── layout/ # Layout & structure components
│ │ ├── container.tsx # Content width constraint
│ │ ├── section.tsx # Section wrapper with spacing
│ │ └── header.tsx # Site header with navigation
│ ├── brand/ # Branding elements
│ │ └── logo.tsx # Theme-aware logo component
│ ├── content/ # Content-specific components
│ │ ├── post-content.tsx # MDX content wrapper
│ │ ├── post-image.tsx # Post images with aspect ratio
│ │ └── transformer-copy-button.tsx # Code block copy functionality
│ ├── sections/ # Page-specific sections
│ │ └── home/ # Homepage sections
│ │ ├── article-section.tsx
│ │ ├── featured-project-section.tsx
│ │ └── profile-section.tsx
│ ├── theme/ # Theme system
│ │ ├── theme-provider.tsx
│ │ └── theme-switcher.tsx
│ └── ui/ # Reusable UI primitives (shadcn/ui)
│ ├── button.tsx
│ ├── card.tsx
│ ├── avatar.tsx
│ ├── aspect-ratio.tsx
│ └── skeleton.tsx
└── mdx-components.tsx # Global MDX component mapping
public/
└── images/ # Static assets (Next.js requirement)
├── avatar.png # Global assets
├── featured-projects/ # Organized by feature
│ ├── bookmark_landing_page.png
│ ├── room_homepage.png
│ └── [other project images]
└── posts/ # Organized by post
└── hiding-scrollbars-in-tailwind/
├── image_00.jpg
└── image_01.jpg
- Better discoverability - Components grouped by purpose
- Logical structure - Related components together
- Scalable architecture - Easy to add new content types
- Clear separation - Layout, content, and UI components distinct
Static assets must be in the public/ directory due to Next.js requirements:
- Next.js Static Assets: Only files in
public/are served as static assets - Build Process: Next.js optimizes and serves these files at build time
- Hot Reloading: Dev server watches
public/for changes - Deployment: Static assets are deployed to CDN/edge locations
Organization Strategy:
public/images/featured-projects/- Project showcase imagespublic/images/posts/[slug]/- Post-specific imagespublic/images/- Global assets (avatar, icons, etc.)
This project uses Contentlayer2 for type-safe MDX content management:
- Content Directory: All MDX posts are stored in
content/posts/ - Type Safety: Contentlayer generates TypeScript types from your content
- Build Process: Run
pnpm contentlayerto build content or it runs automatically duringpnpm build - Frontmatter: Posts require
title,topic,datefields; optionaldescriptionandpublishedfields - Computed Fields: Automatically generates
slug,url, andreadingTimefor each post
The src/mdx-components.tsx file must remain at the src/ level due to Next.js conventions:
- Next.js automatically discovers this file for MDX configuration
- Required for App Router MDX integration
- Must be named exactly
mdx-components.tsx/js - Cannot be moved to subdirectories like
src/components/
This file provides global component mapping for all MDX files in the application.
pnpm dev- Start development server with Turbopackpnpm build- Build production bundlepnpm lint- Run ESLintpnpm start- Start production serverpnpm storybook- Start Storybook development server on port 6006pnpm build-storybook- Build static Storybook for deployment
- Framework: Next.js 16 (16.0.10) with App Router
- React: React 19 (19.2.3)
- Language: TypeScript 5.7.2
- Styling: Tailwind CSS 3.4.17
- Content: Contentlayer2 (0.5.8) with MDX
- UI Components: Radix UI primitives + Base UI
- Code Highlighting: rehype-pretty-code with Shiki 1.26.1
- Component Development: Storybook 10.1.10 with Vite 7
- Package Manager: pnpm 9.13.2
Configuration files are organized in the config/ directory:
config/components.json- shadcn/ui configuration
This project uses Storybook 10.1.10 for component development and documentation.
pnpm storybookThis will start the Storybook development server at http://localhost:6006.
To build a static version of Storybook:
pnpm build-storybookThe static files will be generated in the storybook-static/ directory.
Stories are located alongside their components in src/components/ui/. To create a new story:
- Create a file named
[component].stories.tsxnext to your component - Use the CSF (Component Story Format) 3.0 syntax
- Export a default meta object and individual story objects
Example:
import type { Meta, StoryObj } from "@storybook/react";
import { YourComponent } from "./your-component";
const meta = {
title: "UI/YourComponent",
component: YourComponent,
parameters: {
layout: "centered",
},
tags: ["autodocs"],
} satisfies Meta<typeof YourComponent>;
export default meta;
type Story = StoryObj<typeof meta>;
export const Default: Story = {
args: {
// your component props
},
};.storybook/main.ts- Main Storybook configuration with Vite builder.storybook/preview.tsx- Global decorators, parameters, and theme setup
Note: This project uses @storybook/react-vite with Vite 7 for faster builds and hot module replacement.
-
Create the MDX File:
mkdir -p public/images/posts/[post-slug] touch content/posts/[post-slug].mdx
-
Add Frontmatter and Content:
--- title: "Your Post Title" topic: "Your Topic" date: "2024-01-01" description: "Optional description for SEO" published: true --- Your post content here... <PostImage src="/images/posts/[post-slug]/image.jpg" />
-
Build Content: Run
pnpm contentlayerto generate types and build the content, or it will run automatically duringpnpm build -
Add Images: Place any images in
public/images/posts/[post-slug]/and reference them using/images/posts/[post-slug]/filename.jpg
Note: Posts are automatically discovered by Contentlayer from the content/posts/ directory. No manual listing required!
-
Add Project Images: Place project images in
public/images/featured-projects/ -
Update Featured Projects: Edit
src/components/sections/home/featured-project-section.tsxand add your project to theprojectsarray:{ title: "Your Project Title", description: "Project description...", tags: ["Next.js", "Tailwind", "TypeScript"], image: "/images/featured-projects/your-project-image.png", link: "https://your-project-link.com", }
- Image Formats: Use
.pngfor UI screenshots,.jpgfor photos - Naming Convention: Use lowercase with hyphens (e.g.,
my-project-image.png) - Organization: Group related images in appropriate subdirectories
- Optimization: Consider image optimization for web (WebP, appropriate sizing)
[x] create header elements [x] create code blocks [x] create bulleted lists [x] Upgrade to Next15 [x] Cleaned up the Posts page [x] Remove blog components we no longer need [x] Reorganize component structure [x] Reorganize asset structure for better maintainability [] Add link back to Posts page from the individual post [] create images with captions [] create quote/info sections [] use same icon strategy as Build UI [] post: Ergonomic Interactions example for input with icon https://devouringdetails.com/principles/ergonomic-interactions