Personal portfolio website for Au Duong Tuan, featuring a sophisticated content management system using Notion as a headless CMS.
๐ Live Site: auduongtuan.com
- Framework: Next.js 15.3.1 (Pages Router)
- Language: TypeScript 5.7.2 with strict typing
- Styling: Tailwind CSS 4.1.5 with custom design system
- Package Manager: Bun
- CMS: Notion API (headless CMS)
- State Management: Zustand
- Data Fetching: SWR with custom caching layer
- Image Optimization: Cloudinary + Next.js Image
- AI: Google Gemini SDK for comment suggestions
- Analytics: Google Analytics (gtag)
- UI Components: Base UI (accessible component library)
- ๐ Dynamic Content: Projects, blog posts, and portfolio powered by Notion
- ๐ฌ Interactive Comments: AI-powered suggestions with Gemini
- ๐ Reactions System: Like/reaction functionality for posts
- ๐ต Spotify Integration: Real-time currently playing display
- ๐ผ๏ธ Smart Media Handling: Automatic image optimization with Cloudinary
- ๐ Password Protection: Private content support
- ๐ฑ Fully Responsive: Mobile-first design approach
- ๐จ Custom Design System: Tailwind-based with atomic design structure
- ๐ Dynamic OG Images: Generated social sharing cards
โโโ components/ # Atomic Design Structure
โ โโโ atoms/ # Basic UI (Button, Badge, CustomImage)
โ โโโ molecules/ # Composite (Navigation, Footer, Cards)
โ โโโ templates/ # Page templates
โ โโโ notion/ # Notion content rendering
โโโ lib/ # Business Logic
โ โโโ notion/ # Notion API integration & types
โ โโโ utils/ # Utility functions & cache
โ โโโ cloudinary.ts # Image optimization
โ โโโ gtag.ts # Analytics
โโโ pages/ # Next.js Pages Router
โ โโโ api/ # API routes
โ โโโ blog/ # Blog pages
โ โโโ project/ # Project pages
โโโ store/ # Zustand state stores
โโโ hooks/ # Custom React hooks
โโโ public/ # Static assets
"@atoms/*": "components/atoms/*"
"@molecules/*": "components/molecules/*"
"@templates/*": "components/templates/*"
"@lib/*": "lib/*"
"@store/*": "store/*"- Bun (recommended) or Node.js 18+
- Notion API key and database IDs
- Cloudinary account (for image optimization)
- Google Gemini API key (for AI features)
- Clone the repository:
git clone https://github.com/auduongtuan/auduongtuan.com.git
cd auduongtuan.com- Install dependencies:
bun install- Set up environment variables:
cp .env.example .env.local- Configure your
.env.local:
# Notion
NOTION_API_KEY=your_notion_api_key
PROJECT_DATABASE_ID=your_project_database_id
PROJECT_GROUP_DATABASE_ID=your_project_group_database_id
BLOG_DATABASE_ID=your_blog_database_id
# Notion Data Sources (for comments, reactions, metadata)
COMMENT_DATASOURCE_ID=your_comment_datasource_id
REACTION_DATASOURCE_ID=your_reaction_datasource_id
METADATA_DATASOURCE_ID=your_metadata_datasource_id
# Cloudinary
CLOUDINARY_CLOUD_NAME=your_cloud_name
CLOUDINARY_API_KEY=your_api_key
CLOUDINARY_API_SECRET=your_api_secret
# Google Gemini
GEMINI_API_KEY=your_gemini_api_key
# Site Configuration
NEXT_PUBLIC_PRODUCTION_WEB_URL=https://auduongtuan.comRun the development server:
bun run devOpen http://localhost:7777 to see the result.
Run with cache revalidation (bypasses Notion data cache):
bun run dev --revalidate-cache
# or
bun run dev --revalidate
# or
bun run dev -rThe dev server uses Turbopack for faster builds and runs on port 7777.
# Build for production
bun run build
# Start production server
bun run start
# Lint
bun run lintThe project implements a custom caching layer for Notion data in development:
- Cache TTL: 24 hours
- Cache Location: In-memory (NodeCache)
- Cached Data: Projects, posts, and other Notion database queries
- Environment: Development only (production always fetches fresh data)
Use the --revalidate-cache, --revalidate, or -r flags to bypass cache:
bun run dev --revalidate-cacheImplementation: lib/utils/cache.ts:shouldRevalidateCache()
- Dynamic Content: Projects and blog posts fetched from Notion databases
- Rich Content: Support for headings, lists, callouts, toggles, embeds, code blocks
- Smart Mentions: Link previews, user mentions, page mentions
- Embed Enhancements: Custom dimension extraction from captions (e.g.,
[542-504] Description)
- AI Suggestions: Gemini-powered comment suggestions (Vietnamese & English)
- Data Fetching: useSWR for automatic caching and revalidation
- Rate Limiting: 5 requests per minute per IP
- Storage: Notion database
- Emoji Reactions: ๐ ๐ ๐ฎ ๐ ๐คจ
- Optimistic Updates: Immediate UI feedback with background sync
- Animations: emoji-blast effects on interaction
- Cloudinary: Primary image CDN with automatic optimization
- Next.js Image: Built-in optimization fallback
- Sharp: Server-side processing
- Remote Patterns: AWS, Cloudinary, Spotify, YouTube
/api/comment- Comment CRUD operations/api/comment-suggestion- AI-powered comment suggestions/api/reaction- Reaction system/api/spotify- Currently playing track/api/og/[...data]- Dynamic OG image generation
The site is optimized for Vercel deployment:
- Push to GitHub
- Import project to Vercel
- Configure environment variables
- Deploy
Automatic deployments are triggered on push to main branch.
- Pages Router: This project uses Next.js Pages Router, NOT App Router
- TypeScript: Strict mode enabled, full type coverage expected
- Mobile-First: Responsive design approach
- Accessibility: Using Base UI for accessible components
- Cache Behavior: Development cache only, production always fresh
- Dev Server: Runs on port 7777 with Turbopack enabled
This is a personal portfolio project, but suggestions and bug reports are welcome via issues.
All rights reserved ยฉ Au Duong Tuan