DEV Community

Cover image for How I Built My Portfolio Site
Antarr Byrd
Antarr Byrd

Posted on

How I Built My Portfolio Site

From a Gatsby Fork to a Custom Next.js Stack

Like a lot of developers, I'd been putting off rebuilding my portfolio for years. The old site was a fork of Brittany Chiang's v4 portfolio — a beautiful Gatsby setup that I'd been patching and personalizing for a while. It worked, but the codebase was aging. Dependencies were years out of date, Node 10 was the pinned version, and getting a clean local build required jumping through hoops.

Eventually I decided to start fresh and build something I actually understood end-to-end. Here's what I used and why.


The Stack

Next.js 16

I chose Next.js for the rebuild. It's what I'm most productive in for React-based sites, and the App Router makes server-side rendering and metadata management straightforward. The portfolio is a static-ish site — no database, no auth — so Next.js might feel like overkill, but the ecosystem tooling (image optimization, font loading, structured data in <head>) made it worth it.

One gotcha: Netlify blocked deployment because the initial version used a Next.js release affected by CVE-2025-55182. Keeping dependencies up to date matters even on personal projects.

Tailwind CSS 4

Styling is handled entirely with Tailwind CSS. Version 4 drops the tailwind.config.js file in favor of CSS-native configuration, which I found cleaner. The utility-first approach lets me move fast without context-switching between files.

Framer Motion

Animations are handled by Framer Motion. The hero section fades and slides in on load — nothing too aggressive, just enough to give the page some life. The motion component API keeps animations colocated with the markup rather than scattered across stylesheets.

YAML Content Files

All projects and experience entries live in YAML files under content/. A js-yaml utility reads them at build time and passes the data into the page components. This keeps content completely separate from presentation — adding or removing a project is just editing a file, no component changes needed.

# content/projects/example.yaml
title: "My Project"
description: "|"
  A short description of what this does.
tags:
  - Ruby on Rails
  - PostgreSQL
Enter fullscreen mode Exit fullscreen mode

TypeScript

The whole codebase is TypeScript. Nothing exotic — mostly typed props and API response shapes. It pays for itself quickly when you're refactoring content structures.


Deployment: Netlify

The site deploys automatically via Netlify on every push to main. Build settings are defined in netlify.toml:

[build]
  command = "npm run build"
  publish = ".next"

[build.environment]
  NODE_VERSION = "22"

[[plugins]]
  package = "@netlify/plugin-nextjs"
Enter fullscreen mode Exit fullscreen mode

Pinning the Node version in netlify.toml is important — without it, Netlify defaults to an older version that may not match your local environment.


SEO & Structured Data

I added a Person JSON-LD block in the <head> to help Google's Knowledge Graph associate my professional profiles with my domain:

{
  "@context": "https://schema.org",
  "@type": "Person",
  "name": "Antarr Byrd",
  "url": "https://antarr.dev",
  "sameAs": [
    "https://www.linkedin.com/in/antarrbyrd",
    "https://github.com/antarr"
  ],
  "worksFor": {
    "@type": "Organization",
    "name": "Jay Bird Consulting Group LLC"
  }
}
Enter fullscreen mode Exit fullscreen mode

You can validate your own with Google's Rich Results Test. Note: Person schema won't show as a "rich result" in that tool — use the legacy Structured Data Testing Tool to see it parsed correctly.


Node Version Management: fnm

I use fnm (Fast Node Manager) instead of nvm. It's faster, written in Rust, and reads .nvmrc files automatically. The project pins Node 22 in .nvmrc:

22.20.0
Enter fullscreen mode Exit fullscreen mode

What I'd Do Differently

  • Start with a clean repo. The old site was a GitHub fork, which meant I couldn't fully detach from the upstream fork network. Starting fresh in a new repo avoided all that overhead.
  • Pin Node early. A lot of early build failures came down to running the wrong Node version locally versus what Netlify expected.
  • Use YAML for content from day one. Keeping content in structured files rather than hardcoded JSX made it easy to iterate on what to show publicly.

The full source is private, but feel free to reach out if you have questions about any part of the setup.

Top comments (0)