trahoangdev
All projects
Role
Owner
Team
Solo
Reading time
4 min read

Personal Portfolio

This website: a high-performance portfolio built with Next.js 16 and Tailwind CSS 4, with a feature-based architecture, a file-based MDX blog, RSS, structured data, a Redis-backed visitor counter and an automated test and lint pipeline.

  • Next.js 16
  • React 19
  • Tailwind CSS 4
  • TypeScript
  • Redis
  • Jest
Personal Portfolio preview

Overview

This is the site you are reading. It is my digital garden — projects, a technical blog, experience and certificates — and also a deliberate exercise in building something small properly: clear structure, tested logic, safe defaults and no dead code.

Stack

Next.js 16 (App Router) and React 19 with TypeScript, Tailwind CSS 4 for styling, Upstash Redis for the visitor counter, react-markdown with remark-gfm for content, and Jest with Testing Library for tests. It deploys on Vercel with Vercel Analytics and Speed Insights.

Architecture: features, not layers

The code is organized by feature rather than by technical layer. Each slice owns its components and, where there is real logic, a small domain / application / infrastructure split.

From content to production
  1. Content
    • MDX filescontent/posts · content/projects
    • Typed data filestools · certificates · honors
  2. Features
    • Blog service
    • Projects service
    • Tools & systemPorts and adapters
  3. App Router
    • Static pagesblog · projects · experience
    • Route handlersRSS · sitemap · OG image · visitor API
  4. Platform
    • VercelHosting · Analytics · Speed Insights
    • Upstash RedisVisitor counter and rate limit
  • Data store
  • Compute / service

The tools and diagnostics features use ports and adapters (for example a ToolRepository interface with a static implementation), which keeps the logic framework-free and testable without rendering anything. Simple features are just data plus components — layers are added only when they pay for themselves.

Content as files

Blog posts and these case studies are MDX files with front matter parsed by gray-matter. A small service reads them at build time, validates the slug and required fields, and the pages are statically generated. Both pages share one article renderer, so typography, the table of contents and code blocks behave the same everywhere. Content changes are commits, which means history, review and rollback come for free.

SEO and sharing

Per-page metadata and canonical URLs, a generated sitemap and robots file, an RSS feed, JSON-LD for the person, articles and breadcrumbs, and a 1200×630 Open Graph image. JSON-LD is serialized through a helper that escapes it before injection.

Security and reliability

  • Security headers configured in next.config.ts: HSTS, Content-Security-Policy, frame options, referrer policy and permissions policy.
  • Blog and project slugs are validated against a strict pattern before they touch the filesystem — a fix that came out of a security review that also patched vulnerable Next.js versions.
  • The visitor counter uses Redis INCR for atomic increments, and rate limiting with TTL keys per hashed IP, which suits serverless runtimes where in-memory counters would not survive.
  • Route-level and global error boundaries show friendly messages and log structured context.

Engineering discipline

The pipeline is lint with zero warnings allowed, type-check, and a Jest suite covering domain objects, application services, hooks, key components, the Redis wrapper and the visitor API. Beyond the features, a good share of the work was removing things: a legacy project-catalog stack that was no longer mounted, duplicate stylesheets, an unused UI kit and two overlapping icon libraries. Less code means a smaller surface to secure and understand.

What I took away

A portfolio is the rare project where the author is also the maintainer, reviewer and user. That makes it a good place to practise the habits that matter on teams: small modules, honest documentation that matches the code, tests around the logic that can break, and the discipline to delete what is no longer used.