Migrating to Astro
Why
The site was a React SPA — everything, including the nav and page shell, was rendered by JavaScript. This caused visible layout shift on every page load: the nav would pop in after hydration, fonts would settle, widgets would jump into place. Fine functionally, but jarring.
The goal was a page that loads like it was never not there.
What changed
Migrated to Astro with React islands. Static content — nav, profile, bio, skills — is now plain HTML from byte one. Interactive parts (weather, Spotify, mood, clock, command palette) stay as React components but only hydrate where needed.
Key wins:
- Nav is in the initial HTML, zero JS required for it to appear
- Active link state is resolved at build time via
Astro.url.pathname - Theme toggle appearance is CSS-driven (
[data-theme]on<html>), set by an inline script before first paint — no flash, no animation on load - compute at build time, not runtime, more pure HTML, less js
Infra unchanged: Cloudflare Pages + Workers handles the API backend the same way. The Astro output is static, Workers serve the dynamic API routes (/api/*).
Trade-offs
Blog post content renders in Astro templates, not React islands — MDX’s JSX runtime doesn’t mix cleanly with React islands. A minor constraint with no user-visible impact.