all posts
May 12, 2026·2 min read·portfolionextjsr3fai

Building zaidxshaikh: foundation → viral hooks in one week

How v2 of my portfolio went from empty branch to WebGL + ⌘K palette + streaming AI chat + audience switching, in a week of focused work.

A portfolio that looks like every other portfolio is a portfolio nobody remembers. The brief for v2 was simple: make people screenshot it within ten seconds of landing.

This is the rough log of how it came together.

The stack decision

Next.js 15 + React 19         framework + concurrent rendering
Tailwind 4 (@theme OKLCH)     CSS-first config, perceptually uniform colors
Three.js + R3F + drei         3D hero
Framer Motion 12              UI micro-interactions
Zustand 5                     UI state
cmdk + nuqs                   command palette + URL state
Vercel AI SDK + @ai-sdk/groq  streaming AI chat
Upstash Redis                 sliding-window rate limit
Resend + Cloudflare Turnstile contact form + anti-bot

OKLCH tokens were the unsung hero — perceptually uniform color spaces make the 6-theme studio (which came later) trivial. Lightening or shifting hue stays visually consistent across themes.

The four hooks

I designed the site around four "screenshot moments":

  1. The 3D hero — a glyph field with 20 project orbs in a logarithmic spiral. The wordmark zaidxshaikh floats at origin. Click an orb → smooth view transition to the case study.
  2. ⌘K command palette — fuzzy search over every project, action, deep-link. URL state via nuqs so ?cmd=hire-me deep-links into a pre-filtered palette.
  3. Streaming AI chat — Groq Llama 3.3 70B via Vercel AI SDK, grounded in my actual resume via a ~6KB system prompt. Sub-300ms TTFT. Refuses jailbreaks, rate-limited.
  4. Audience switching?as=recruiter|dev|client retints the 3D scene, swaps the hero copy, changes the primary CTA, and shifts the AI's system prompt. Triptych screenshot = comparison shot for virality.

Build-time RAG, no vector DB

For the AI chat I considered the standard "vector DB + embeddings" route. At resume scale (~6 KB of source material) that's overkill. Instead, a build script flattens profile.ts + projects.ts + experience.ts + case-studies.ts + services.ts into a single markdown blob, which becomes the system prompt at request time.

Cheaper, faster, and removes Pinecone/Weaviate as a dependency. Doesn't scale past ~20 KB of source, but for a personal site it's perfect.

Bugs along the way

  • React 19 + R3F v9 mismatch — the initial scaffold pinned React 18, R3F 8. Production crashed with Cannot read properties of undefined (reading 'ReactCurrentBatchConfig'). Fix: bump to React 19 + R3F 9 + drei 10.
  • Scroll capture bug — covered in a separate post on removing Lenis.
  • Suspense boundary errorsuseSearchParams via nuqs requires a Suspense boundary in App Router. Wrapped <Nav /> and <CommandPalette /> accordingly.

What the week shipped

By end of week one: WebGL hero, ⌘K palette, AI chat, audience switching, 20 projects with full case studies, services + pricing, contact form, resume page, sitemap + robots + JSON-LD, dynamic OG images, Vercel deploy with env vars on Production + Preview.

The fun part is what came after: theme studio (6 themes), Konami easter egg, view transitions, sound design, PWA + offline shell. Each one earns its own post.

The whole repo is public: github.com/zaidxshaikh/shaikh-zaiid.github.io.