Building a Full-Stack Personal Blog: Architecture, Performance & Modern Next.js

#project#nextjs#typescript#performance#portfolio#server-components

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: PostFrontmatter interface guarantees correct title, date, tags, and description fields 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-country header → automatic locale selection on first visit.

Page Transitions & Micro-interactions

Smooth page transitions powered by Framer Motion:

  • Page-level: AnimatePresence with blur + fade + slide effects on route changes
  • Staggered children: MotionItem components 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/, and app/

This blog is my digital garden — a place where I write about web performance, modern React patterns, and lessons learned while building in public.

Read article

Blog