How I Built My Production-Ready Next.js Portfolio — Moataseem Shaaban

How Moataseem Shaaban built a Next.js portfolio with static rendering, Tailwind CSS, GSAP and Nginx, with an update on its current architecture.

Layered architectural model of a portfolio website
A portfolio assembled as a complete system, from interface to infrastructure. AI-generated editorial illustration, not a product screenshot.

I'm Moataseem Shaaban, a full stack developer based in Cairo, Egypt. When I decided to rebuild my portfolio from scratch, I wanted it to be more than a pretty page — I wanted it to be a showcase of real engineering. In this post, I'll walk you through exactly how I built moataseem.com using Next.js 15, Tailwind CSS v4, GSAP, and deployed it on my own hardened Ubuntu VPS.

September 2026 architecture update

This article documents the original Next.js 15 build. The current portfolio uses Next.js 16.3.5 and React 19.3.0. Public pages still use static exports served by Nginx. The private Brand Studio has a separate authenticated Node.js service for saved content and scheduling. The examples below describe the original implementation.

Why I Chose Next.js for My Portfolio

As a full stack developer who works with React daily, Next.js was the natural choice for my portfolio at moataseem.com. With features like static export, automatic code splitting, and built-in image optimization, it gives you everything you need to build a fast, SEO-friendly portfolio.

For my portfolio specifically, I used the static export feature (output: 'export'). It generates plain HTML, CSS, and JavaScript files that I serve from my own Nginx server — no Node.js runtime required in production.

Key Advantages

  • ●Static Generation: Pre-renders every page at build time, resulting in instant load times
  • ●Automatic Code Splitting: Only loads the JavaScript needed for each page
  • ●Built-in SEO Support: The Metadata API makes it trivial to add Open Graph tags, Twitter cards, and structured data
  • ●React 19 Support: Server Components, improved hydration, and better performance out of the box

Project Setup and Architecture

Start by creating a new Next.js project with the App Router:

bash
npx create-next-app@latest portfolio --app --tailwind --eslint

I recommend organizing your project like this:

src/
├── app/
│   ├── layout.jsx        # Root layout with metadata
│   ├── page.jsx           # Home page
│   └── blog/
│       ├── page.jsx       # Blog listing
│       └── [slug]/
│           └── page.jsx   # Individual posts
├── components/            # Reusable UI components
├── common/
│   └── constants/         # Metadata, blog data
├── hooks/                 # Custom React hooks
├── lib/                   # Utility functions
└── assets/                # Images, fonts

This structure separates concerns cleanly and scales well as your portfolio grows.

How I Styled It with Tailwind CSS v4

For moataseem.com, I went with Tailwind CSS v4 — the ground-up rewrite with a Rust-based engine. The new @theme directive lets you define design tokens directly in CSS, which I found much cleaner than the old config file approach:

css
@import "tailwindcss";

@theme inline {
  --color-primary: oklch(0.7 0.15 160);
  --color-background: oklch(0.145 0 0);
  --color-foreground: oklch(0.985 0 0);
  --radius: 0.625rem;
}

The OKLCH color space is perceptually uniform, meaning colors look consistent across different hues and lightness levels. This is a significant upgrade over HSL for building cohesive color palettes.

My Dynamic Theme Selector

One feature I'm proud of on moataseem.com is the dynamic color theme selector — visitors can choose their preferred accent color and it persists across visits. The trick is using CSS custom properties that can be updated at runtime:

javascript
document.documentElement.style.setProperty('--color-primary', newColor);
localStorage.setItem('portfolio-theme', themeName);

This approach gives you theme persistence without any server-side logic.

How Moataseem Shaaban Uses GSAP for Portfolio Animations

Animations make or break a portfolio's first impression. On moataseem.com, I use GSAP (GreenSock Animation Platform) for all complex animations because it outperforms CSS animations in control and cross-browser consistency.

Key animation patterns I built for my portfolio:

  • ●Entrance animations: Elements fade in and slide up as sections enter the viewport using ScrollTrigger
  • ●Parallax effects: Background elements move at different speeds based on scroll position
  • ●Magnetic cursor: Interactive elements that subtly attract toward the cursor on hover
  • ●Staggered reveals: Lists and grids animate item by item for a polished feel
javascript
gsap.registerPlugin(ScrollTrigger);

gsap.from('.section-title', {
  y: 60,
  opacity: 0,
  duration: 1,
  scrollTrigger: {
    trigger: '.section-title',
    start: 'top 80%',
  }
});

SEO Optimization

For a portfolio, SEO is critical. Here's a comprehensive checklist:

Metadata API

Next.js 15's Metadata API is powerful. Define your metadata in a constants file and import it:

javascript
export const metadata = {
  title: 'Your Name — Full Stack Developer',
  description: 'Your compelling description here',
  metadataBase: new URL('https://yourdomain.com'),
  alternates: { canonical: '/' },
  openGraph: {
    title: 'Your Name — Portfolio',
    images: [{ url: '/og-image.png', width: 1200, height: 630 }],
  },
};

Structured Data (JSON-LD)

Add Person, WebSite, and ProfilePage schemas to help search engines understand your content:

jsx
<script type="application/ld+json">
  {JSON.stringify({
    "@context": "https://schema.org",
    "@type": "Person",
    "name": "Your Name",
    "jobTitle": "Full Stack Developer",
    "url": "https://yourdomain.com",
    "knowsAbout": ["React", "Next.js", "Node.js"]
  })}
</script>

Technical SEO Checklist

  1. 1.Canonical URLs — Prevent duplicate content issues
  2. 2.robots.txt — Allow crawlers and reference your sitemap
  3. 3.sitemap.xml — List all your pages with last modified dates
  4. 4.HSTS headers — Signal trust to search engines
  5. 5.Core Web Vitals — Optimize LCP, FID, and CLS scores
  6. 6.Mobile-first — Google uses mobile-first indexing

Deployment on a VPS

While platforms like Vercel are convenient, deploying on your own VPS gives you full control over performance tuning:

  1. 1.Build locally: npm run build generates static files in the out/ directory
  2. 2.Upload to server: Use rsync to sync files to your web root
  3. 3.Nginx configuration: Serve static files with aggressive caching
  4. 4.SSL with Certbot: Free HTTPS certificates from Let's Encrypt
  5. 5.Gzip compression: Reduce file sizes by 60-80%

Nginx Performance Config

nginx
location /_next/static/ {
    expires 1y;
    add_header Cache-Control "public, immutable";
}

location ~* \.(jpg|jpeg|png|gif|ico|svg|webp|woff|woff2)$ {
    expires 1y;
    add_header Cache-Control "public, no-transform";
}

Measuring portfolio performance

Performance changes as content, dependencies and hosting configuration evolve. A Lighthouse result describes one test run; it is not a permanent score or a guarantee of every visitor's experience.

For each release, test the live mobile page, inspect JavaScript and CSS compression, check the largest visible content and layout shifts, and compare the result with real-user Core Web Vitals when enough field data is available. Keep the report date and test settings with any published measurements. The current workflow is covered in my React performance guide.

Conclusion — Moataseem Shaaban's Approach to Portfolio Engineering

Building my portfolio at moataseem.com taught me that a great developer portfolio is about balancing aesthetics with engineering. I used Next.js for the framework, Tailwind CSS v4 for styling, GSAP for animations, and deployed on my own hardened VPS for full control.

If you're a developer building your own portfolio, include performance, accessibility and SEO in the first release. This article explains the implementation; the portfolio repository is private. You can explore my public GitHub profile and selected projects for other work.

I'm Moataseem Shaaban — if you have questions about this build or want to collaborate, reach out through my contact page.