Migrating a Portfolio Site to Astro

How and why I rewrote my portfolio from a jQuery HTML5 template to Astro — and what I learned along the way.

astrowebportfolio

Why Astro?

After running the same HTML5 UP “Dimension” template for years, I decided it was time for a proper rewrite. The goals were:

  • Faster load times — zero JS shipped by default
  • Blog support — markdown-driven, no CMS needed
  • Maintainable architecture — components, typed data, clean separation of concerns
  • Same dark aesthetic — keep what works, modernize what doesn’t

Astro delivers all of this out of the box.

What Changed

The old site was a single index.html with jQuery handling navigation, a custom text animator, and article flip transitions. Content was hard-coded into HTML.

The new site:

  • All content lives in typed JSON data files (experience.json, projects.json)
  • Skills are loaded from skills.json — already existed, just wired up properly
  • Each section is an isolated Astro component
  • Blog posts are Markdown files in src/content/articles/
  • Zero jQuery — just a tiny vanilla JS snippet for the name reveal animation

The Blog System

This is just a Markdown file. Drop a .md file in src/content/articles/ with frontmatter:

---
title: "Your Title"
date: 2025-05-13
description: "One liner"
tags: ["tag1", "tag2"]
---

Astro Content Collections validates the frontmatter, generates the route at /blog/[slug], and renders it with full syntax highlighting. No config needed beyond the initial config.ts schema.

What Stayed

The sub-apps — /mealplanner, /cah, /wordwiz — are untouched vanilla JS apps. They live in public/ and are served directly by nginx, completely bypassing Astro’s build pipeline.

The Docker + nginx setup is largely unchanged. Astro outputs a static dist/ folder; the Dockerfile just COPYs that instead of the entire project root.