How I Built This Portfolio With Next.js and Firebase
A technical walkthrough of building a cinematic developer portfolio with Next.js 16, Tailwind CSS v4, Framer Motion animations, MDX blog support, and Firebase Hosting deployment.
title: "How I Built This Portfolio With Next.js and Firebase" date: "2024-11-15" description: "A technical walkthrough of building a cinematic developer portfolio with Next.js 16, Tailwind CSS v4, Framer Motion animations, MDX blog support, and Firebase Hosting deployment." tags: ["nextjs", "firebase", "blog", "react"] published: true image: ""
I wanted a portfolio that didn't look like every other developer's site. No stock templates, no boring grids. Something that felt like me — dark, animated, technically interesting to look at.
Here's how I built it.
Tech stack decisions
The core choices:
| Layer | Choice | Why |
|---|---|---|
| Framework | Next.js 16 | App Router, static export, MDX support |
| Styling | Tailwind CSS v4 | CSS variables + utility classes, no config file |
| Animation | Framer Motion | Scroll-triggered reveals, spring physics |
| Blog | @next/mdx + Shiki | Build-time compilation, zero client JS for syntax highlighting |
| Deploy | Firebase Hosting | Free tier, global CDN, fast |
The cinematic design system
The visual identity is built around a dark color palette with a green accent:
Tailwind v4 (the new version without a config file) lets you register these as utility classes using the @theme block:
This means theme switching is a single data attribute on <html>:
Floating shapes and glows
The hero section background has 16+ floating geometric shapes, each with its own CSS animation:
Each <FloatingShape> component just renders a CSS shape (ring, dot, cross, triangle, diamond, hexagon) with will-change: transform for GPU compositing and a unique animation-delay so they don't all move in sync.
The glow orbs are blurred radial gradients — visually impactful, zero JavaScript:
MDX blog with build-time syntax highlighting
The blog uses @next/mdx for compilation. Posts live in content/posts/*.mdx and are compiled at build time by webpack.
Syntax highlighting is handled by rehype-pretty-code + Shiki. The highlight is added as inline styles at build time — no client-side JS required:
For the blog listing page, I use gray-matter + Node's fs module to read frontmatter at build time:
Important:
fsonly runs server-side at build time. Never import this into a client component.
Scroll animations with Framer Motion
Cards and sections reveal as you scroll using useInView:
The [0.16, 1, 0.3, 1] cubic bezier is an "overshoot and settle" curve that feels snappy without being jarring.
Static export + Firebase
Since Firebase Hosting serves static files, the site exports to HTML:
The GitHub Actions CI/CD pipeline handles deployment on every push to main:
What I'd do differently
Honestly, not much. The main friction point was Tailwind v4 — the docs are still catching up and the @theme syntax for CSS variable registration took some trial and error. If I were doing this again, I'd read the Tailwind v4 alpha docs more carefully first.
The MDX setup was surprisingly smooth once I committed to @next/mdx over next-mdx-remote — the official integration is cleaner and has better TypeScript support.
The site is open source — you can poke around the code and steal whatever's useful.