Building a Full-Stack Personal Blog: Architecture, Performance & Modern Next.js
Building a Full-Stack Personal Blog
A production-grade personal tech blog — built with the latest Next.js 14 App Router, deployed on Vercel, and continuously optimized for real-world performance. This project showcases my ability to design, build, and optimize a modern full-stack web application from the ground up.
Tech Stack at a Glance
| Layer | Technology |
|-------|-----------|
| Framework | Next.js 14 (App Router) |
| Language | TypeScript (strict mode) |
| Styling | Tailwind CSS 3 + @tailwindcss/typography |
| Content | MDX via next-mdx-remote + @next/mdx |
| Animation | Framer Motion 12 |
| Deployment | Vercel (Edge + Serverless) |
| Monitoring | Vercel Speed Insights |
| Architecture | React Server Components (RSC) + Client Components |
Architecture Decisions
React Server Components by Default
All pages are Server Components by default — data fetching happens on the server, zero client-side JavaScript for static content. Only interactive pieces (dark mode toggle, chatbot, page transitions) are marked 'use client'.
app/
├── layout.tsx → RSC — shared layout with grid background
├── page.tsx → RSC — fetches latest posts, renders server-side
├── blog/
│ ├── page.tsx → RSC — paginated post list with tag filtering
│ └── [slug]/
│ ├── page.tsx → RSC — MDX article rendering
│ └── opengraph-image.tsx → Edge Runtime — dynamic OG images
├── about/
│ ├── page.tsx → RSC — about & skills
│ └── projects/ → Client — GitHub API live data
└── photos/ → RSC — static photo gallery
MDX Content Layer — with performance caching
Content is authored in MDX files with YAML frontmatter. A custom lib/posts.ts layer handles:
- Lazy compilation:
getPosts()only compiles MDX for articles visible on the current page — the rest only get frontmatter parsed (sub-millisecond). This cut server CPU by ~60%. - Request deduplication:
React.cache()ensures no MDX file is compiled twice within a single request tree. - Type-safe frontmatter:
PostFrontmatterinterface guarantees correcttitle,date,tags, anddescriptionfields across all posts.
Dark Mode — Zero Flash Architecture
Dark mode preference is handled entirely on the server side: the <html> tag receives the correct class before any client JavaScript executes, eliminating the infamous "flash of unstyled content" (FOUC).
- Default: dark theme
- Persistence ready via cookies (no external dependency)
Internationalization (i18n)
Bilingual support (English / 中文) with a lightweight server-side approach:
// lib/i18n.ts — typed translation map
const translations = {
en: { 'nav.home': 'Home', /* ... */ },
zh: { 'nav.home': '首页', /* ... */ },
}
- No runtime i18n library — translations are plain TypeScript objects, fully type-checked by the compiler.
- Language detection via
x-vercel-ip-countryheader → automatic locale selection on first visit.
Page Transitions & Micro-interactions
Smooth page transitions powered by Framer Motion:
- Page-level:
AnimatePresencewith blur + fade + slide effects on route changes - Staggered children:
MotionItemcomponents animate in with configurable delays, creating a cascading entrance effect for cards and lists - Hover states: Cards scale slightly and shift accent colors on hover
Tailwind Design System
A custom neon-dark aesthetic defined entirely in tailwind.config.ts:
Custom Colors: acid (green palette), neon (pink/blue/purple/green)
Custom Fonts: Clash Display (headings), Satoshi (body), JetBrains Mono (code)
Custom Effects: noise texture, grid backgrounds, glitch, float, scan-line
CSS Variables: --accent, --bg-primary, --text-secondary (theme-agnostic)
Performance Optimizations
| Technique | Impact |
|-----------|--------|
| MDX lazy compilation (parse frontmatter only for list pages) | ~60% less server CPU per request |
| React.cache() for MDX deduplication | Eliminates redundant compilation |
| Server Components by default | Zero JS shipped for static pages |
| OG images on Edge Runtime | Sub-100ms image generation |
| Tailwind darkMode: 'class' + server-side class injection | Zero layout shift on theme change |
Deployment & Monitoring
Deployed on Vercel with:
- Automatic HTTPS + global CDN edge caching
- Git-push CI/CD with zero-downtime deployments
- Vercel Speed Insights integrated for real-user performance monitoring (TTFB, FCP, LCP, INP)
- Edge runtime for dynamic OG images and geo-based routing
What This Project Demonstrates
- Full-stack ownership — from Tailwind design tokens to Vercel production deployment
- RSC mental model — understanding when data should live on the server vs. client
- Performance-first mindset — profiling, identifying bottlenecks, and optimizing intelligently (not prematurely)
- Type safety end-to-end — TypeScript from content frontmatter through UI components
- Clean architecture — separation of concerns with
lib/,hooks/,components/, andapp/
This blog is my digital garden — a place where I write about web performance, modern React patterns, and lessons learned while building in public.