A high-performance portfolio platform built with Next.js 13 App Router, featuring a custom MDX content pipeline, privacy-preserving edge analytics, and interactive UI components.
- Overview
- Architecture
- Key Features
- Tech Stack
- Quick Start
- Development
- What I Learned
- Future Improvements
This portfolio platform is designed to showcase engineering projects and technical skills through an optimized, modern web experience. Built on Next.js 13's App Router with React Server Components, it combines:
- Custom MDX Content Pipeline using Contentlayer with semantic processing
- Edge-based Analytics with Upstash Redis for privacy-preserving view tracking
- Interactive Visualizations including Mermaid diagrams and particle animations
- End-to-end Type Safety via TypeScript and validated content schemas
The platform prioritizes performance, privacy, and developer experience while maintaining a visually stunning interface.
graph TB
subgraph "Content Layer"
MDX[MDX Files<br/>content/projects/*.mdx]
FM[Frontmatter YAML]
Content[Markdown Content]
end
subgraph "Build Time - Contentlayer"
CL[Contentlayer Engine]
Schema[Schema Validation]
Remark[Remark Plugins<br/>remark-gfm]
Rehype[Rehype Plugins<br/>rehype-slug<br/>rehype-mermaid<br/>rehype-pretty-code<br/>rehype-autolink-headings]
JSON[Generated JSON + Types]
end
subgraph "Next.js App Router"
SSR[Server Components]
CSR[Client Components]
Pages["Pages<br/>/projects/[slug]"]
Layouts[Layouts]
end
subgraph "Edge Runtime"
API[API Routes<br/>/api/incr]
Redis[(Upstash Redis)]
end
subgraph "Client Browser"
UI[Interactive UI]
Particles[Particle System]
Mermaid[Mermaid.js Renderer]
Analytics[Analytics Component]
end
MDX --> FM
MDX --> Content
FM --> CL
Content --> CL
CL --> Schema
Schema --> Remark
Remark --> Rehype
Rehype --> JSON
JSON --> SSR
SSR --> Pages
SSR --> Layouts
Pages --> CSR
CSR --> UI
CSR --> Particles
CSR --> Mermaid
CSR --> Analytics
Analytics --> API
API --> Redis
style MDX fill:#f9f,stroke:#333,stroke-width:2px
style CL fill:#bbf,stroke:#333,stroke-width:2px
style SSR fill:#bfb,stroke:#333,stroke-width:2px
style API fill:#fbb,stroke:#333,stroke-width:2px
style Redis fill:#fab,stroke:#333,stroke-width:3px
flowchart LR
subgraph Input
A[MDX File] --> B{Parse}
B --> C[Frontmatter<br/>YAML]
B --> D[Markdown<br/>Content]
end
subgraph Contentlayer
C --> E[Schema Validation]
E --> F{Valid?}
F -->|Yes| G[Type Generation]
F -->|No| H[Build Error]
D --> I[Remark Processing]
end
subgraph "Markdown โ HTML"
I --> J[remark-gfm<br/>GFM Support]
J --> K[rehype-slug<br/>Add IDs]
K --> L[rehype-mermaid<br/>Detect Diagrams]
L --> M{Mermaid<br/>Block?}
M -->|Yes| N[Create Custom<br/>div with data-mermaid]
M -->|No| O[rehype-pretty-code<br/>Syntax Highlighting]
N --> P[rehype-autolink-headings<br/>Link Headings]
O --> P
P --> Q[HTML Output]
end
subgraph Output
G --> R[Typed JSON]
Q --> R
R --> S[.contentlayer/<br/>generated/]
S --> T[Import in<br/>React Components]
end
style A fill:#f9f,stroke:#333,stroke-width:2px
style E fill:#bbf,stroke:#333,stroke-width:2px
style L fill:#bfb,stroke:#333,stroke-width:2px
style R fill:#fbb,stroke:#333,stroke-width:2px
sequenceDiagram
participant User
participant Browser
participant ReportView as ReportView Component
participant EdgeAPI as Edge API<br/>/api/incr
participant Redis as Upstash Redis
User->>Browser: Visit /projects/[slug]
Browser->>ReportView: Component Mounts
ReportView->>ReportView: Check hasReported ref
alt First Mount
ReportView->>EdgeAPI: POST /api/incr<br/>{slug: "project-name"}
EdgeAPI->>EdgeAPI: Extract IP Address
EdgeAPI->>EdgeAPI: SHA-256 Hash IP
EdgeAPI->>Redis: SET deduplicate:{hash}:{slug}<br/>NX EX 86400
alt New Visitor (24h)
Redis-->>EdgeAPI: OK (Key Created)
EdgeAPI->>Redis: INCR pageviews:projects:{slug}
Redis-->>EdgeAPI: New Count
EdgeAPI-->>ReportView: 202 Accepted
else Seen Before
Redis-->>EdgeAPI: NULL (Key Exists)
EdgeAPI-->>ReportView: 202 Accepted<br/>(No Increment)
end
else Already Reported
ReportView->>ReportView: Skip (React Strict Mode)
end
Note over EdgeAPI,Redis: Privacy: Only hashed IPs stored<br/>Deduplication: 24-hour window<br/>No PII or tracking cookies
graph TD
subgraph "Server Components"
Root[layout.tsx<br/>Root Layout]
Home[page.tsx<br/>Home Page]
ProjectLayout[projects/layout.tsx]
ProjectPage["projects/[slug]/page.tsx"]
Header["projects/[slug]/header.tsx"]
Article[projects/article.tsx]
end
subgraph "Client Components"
Particles[particles.tsx<br/>Canvas Animation]
Nav[nav.tsx<br/>Navigation]
Card[card.tsx<br/>Project Cards]
SpotlightBtn[spotlight-button.tsx<br/>Interactive Button]
SkillIcon[skill-icon.tsx<br/>Tech Icons]
Mermaid[mermaid.tsx<br/>Diagram Renderer]
TOC[table-of-contents.tsx<br/>Page Navigation]
ReportView[view.tsx<br/>Analytics Reporter]
MDX[mdx.tsx<br/>MDX Components]
end
subgraph "Shared Utilities"
Mouse[util/mouse.ts<br/>Mouse Tracking]
Analytics[components/analytics.tsx<br/>Vercel Analytics]
end
Root --> Home
Root --> ProjectLayout
ProjectLayout --> ProjectPage
ProjectPage --> Header
ProjectPage --> Article
ProjectPage --> ReportView
Home --> Particles
Home --> Nav
Home --> SpotlightBtn
Home --> SkillIcon
Article --> MDX
Article --> TOC
MDX --> Mermaid
SpotlightBtn --> Mouse
Particles --> Mouse
ProjectPage --> Analytics
style Root fill:#bfb,stroke:#333,stroke-width:2px
style Particles fill:#bbf,stroke:#333,stroke-width:2px
style Mermaid fill:#fbb,stroke:#333,stroke-width:2px
style ReportView fill:#fab,stroke:#333,stroke-width:2px
Full utilization of React Server Components for optimal initial load performance and automatic code splitting.
Semantic content processing with extensible plugin architecture:
- Syntax Highlighting:
rehype-pretty-codewith GitHub Dark theme - Auto-linked Headings: Automatic anchor generation with
rehype-slugandrehype-autolink-headings - GFM Support: Tables, task lists, and strikethrough via
remark-gfm - Custom Mermaid Plugin:
lib/rehype-mermaid.jsintercepts diagram code blocks before syntax highlighting
Privacy-preserving view tracking system:
- Edge Runtime: Runs on Vercel Edge Network for global low-latency
- IP Hashing: SHA-256 hashing ensures no PII storage (
pages/api/incr.ts) - Deduplication: 24-hour Redis key expiration prevents double-counting
- Atomic Operations: Redis
INCRensures accurate concurrent updates
Integrated Mermaid.js support with zoom/pan controls:
- Diagrams defined in markdown code blocks
- Client-side rendering with
mermaid.tsx - Powered by
react-zoom-pan-pinchfor interactive exploration
Premium visual experience:
- Particle System: WebGL-accelerated canvas with mouse magnetism (
particles.tsx) - Spotlight Buttons: Proximity-based hover effects (
spotlight-button.tsx) - Smooth Animations: Framer Motion for declarative transitions
- Responsive Design: Tailwind CSS with mobile-first approach
End-to-end type safety for content:
- Schema Validation: Contentlayer validates frontmatter against TypeScript schemas
- Generated Types: Automatic type generation for content files
- TypeScript Strict Mode: Prevents runtime errors during development
- Next.js 13 - React framework with App Router
- React 18 - UI library with Server Components
- TypeScript 5.2 - Type-safe JavaScript
- Contentlayer 0.3 - Content SDK for type-safe MDX
- MDX - Markdown with JSX components
- Remark / Rehype - Markdown/HTML processors
- Upstash Redis - Serverless Redis for analytics
- Vercel Edge Runtime - Edge computing platform
- Vercel Analytics - Web vitals monitoring
- Tailwind CSS 3.3 - Utility-first CSS framework
- Framer Motion - Animation library
- Lucide React - Icon library
- Mermaid.js - Diagram rendering
- HTML5 Canvas - Particle animations
- react-zoom-pan-pinch - Interactive diagram controls
-
Clone the repository:
git clone https://github.com/omarafify/omarafify.com.git cd omarafify.com -
Install dependencies:
pnpm install
-
Configure environment variables:
Create a
.envfile in the root directory:UPSTASH_REDIS_REST_URL=your-upstash-url UPSTASH_REDIS_REST_TOKEN=your-upstash-token NEXT_PUBLIC_BEAM_TOKEN=optional-analytics-token
Note: Get free Upstash Redis credentials at console.upstash.com
-
Run the development server:
pnpm dev
Open http://localhost:3000 in your browser.
-
Build for production:
pnpm build pnpm start
omarafify.com/
โโโ app/ # Next.js App Router
โ โโโ components/ # React components
โ โ โโโ particles.tsx # Canvas particle system
โ โ โโโ mermaid.tsx # Mermaid diagram renderer
โ โ โโโ mdx.tsx # MDX component mappings
โ โ โโโ ...
โ โโโ projects/ # Projects section
โ โ โโโ [slug]/ # Dynamic project pages
โ โ โโโ page.tsx # Projects listing
โ โโโ layout.tsx # Root layout
โ โโโ page.tsx # Home page
โโโ content/ # MDX content files
โ โโโ projects/ # Project markdown files
โโโ lib/ # Utilities and plugins
โ โโโ rehype-mermaid.js # Custom rehype plugin
โโโ pages/api/ # API routes
โ โโโ incr.ts # Analytics endpoint
โโโ public/ # Static assets
โโโ util/ # Helper functions
โโโ contentlayer.config.js # Content schema & config
โโโ tailwind.config.js # Tailwind configuration
-
Create an MDX file in
content/projects/:--- title: "Your Project Title" description: "Brief description" date: "2024-01-01" published: true tier: "A" category: ["ml", "backend"] techStack: ["Python", "AWS", "Docker"] repository: "https://github.com/username/repo" --- Your project content here...
-
Contentlayer will automatically:
- Validate the frontmatter schema
- Generate TypeScript types
- Process the MDX content
- Make it available in your components
Extend app/components/mdx.tsx to add custom components:
const components = {
h1: (props: any) => <h1 className="custom-h1" {...props} />,
YourComponent: CustomComponent,
};The project uses Rome for formatting:
pnpm fmtAdapting API routes to the Vercel Edge Runtime required fundamental changes:
- No Node.js APIs: Switched from native
cryptoto Web Crypto API - No TCP Connections: Used Upstash's REST API instead of Redis protocol
- Stateless Functions: Designed for instant cold starts and global distribution
Building the custom Mermaid plugin (lib/rehype-mermaid.js) taught me:
- How markdown parsers create Abstract Syntax Trees (ASTs)
- Plugin execution order matters (must run before syntax highlighting)
- Transforming nodes while preserving document structure
Learned the new paradigm of data fetching and rendering:
- Server Components: Fetch data, access databases, render static content
- Client Components: Handle interactivity, use hooks, access browser APIs
- Boundaries: Strategic "use client" directives for minimal client JavaScript
Contentlayer is deprecated. Plan to migrate to:
- Option 1: Next.js built-in MDX support with custom loader
- Option 2: Velite as a direct replacement
- Option 3: Custom build-time processor
Implement client-side fuzzy search:
- Index project metadata (title, description, tech stack)
- Use Fuse.js for fuzzy matching
- Filter and sort projects dynamically
Add comprehensive testing:
- Unit Tests: Jest for utilities and components
- Integration Tests: Test MDX pipeline and content processing
- E2E Tests: Playwright for user flows and analytics verification
- Implement incremental static regeneration (ISR) for projects
- Add image optimization with Next.js Image component
- Optimize particle system with WebGL shaders
Built with โค๏ธ by Omar Afify