DEV Community

MEHTAB RIAZ
MEHTAB RIAZ

Posted on

I shipped my developer portfolio on GitHub Pages with Next.js static export

I recently polished and open-sourced my personal portfolio: a Next.js site with JSON-driven content, static export, and GitHub Actions deploying to GitHub Pages—no server, no API routes at runtime.

If you’re comparing stacks for a portfolio or fighting subpath deploys (username.github.io/repo/), this might save you a few rabbit holes.


Live site & repo


What it is

A single-page style home with hero, featured work, about, technical stack, and experience, plus extra routes for projects (GitHub API at build time) and articles (Medium RSS via rss2json, also at build time). Most copy and structure live under content/ as JSON, so updates don’t always mean digging through JSX.

Stack in practice:

  • Next.js (Pages Router), React, Sass
  • Font Awesome free icons (with a small util to map old Pro-style prefixes to free solid)
  • Framer Motion for motion where it helps
  • Jest + Testing Library for a few unit tests

Why static export + GitHub Pages

I wanted:

  • Free hosting tied to the repo
  • Predictable URLs for a project site under a subpath
  • No need to run Node in production—just static files after next build

next.config.js uses output: 'export' so the app becomes plain HTML/JS/CSS in out/. GitHub Pages serves that output from the workflow in .github/workflows/deploy-github-pages.yml.

The annoying part of project pages is the /portfolio/ base path: assets, favicons, manifests, and client-side public URLs must stay consistent. The repo uses basePath / assetPrefix derived from the GitHub repo name in CI, plus a small publicPath() helper so links to public/ files work both locally (/) and on Pages (/portfolio/).


Content model (quick mental model)

Rough map (details in the README):

Area Where it lives
Settings / GitHub username for APIs content/_settings.json
Hero & page colors content/index/hero.json, content/index/_colors.json
Featured projects content/projects/featured.json
Technical section content/index/technical.json
Experience content/index/experience.json
Footer content/footer.json

That split kept the site maintainable as I iterated on copy and featured work.


Things I’d tell my past self

  1. Static export means no pages/api in the classic sense—anything dynamic either runs at build time (getStaticProps + fetch) or moves to an external service.
  2. images.unoptimized is the usual tradeoff for static hosting without an image CDN.
  3. Subpath deploys are solvable but tedious: manifest icons, document head, and any hardcoded /foo paths will bite you until everything goes through your path helper or Next’s basePath.
  4. Favicons: SVG favicons are great, but external images inside SVG favicons are unreliable in Chromium; inlining the bitmap (e.g. data URI) fixed “blank tab icon” for me.

Try it locally


bash
git clone https://github.com/MehtabRiaz/portfolio.git
cd portfolio
npm install
npm run dev
Enter fullscreen mode Exit fullscreen mode

Top comments (0)