Selected work

tiagogalvao.com: WordPress to Astro Migration

A full rebuild of my own portfolio site, moving tiagogalvao.com from a fully custom WordPress theme to a schema-validated Astro codebase with type-safe content collections, MDX case studies, and a verified 100/100 Lighthouse home page.

Role
Full Stack
Scope
2026 / Website / Migration
Stack
Astro, TypeScript, MDX, Zod, Node.js
a laptop on a desk displaying the new tiagogalvao.com homepage, with the dark hero section and headline Web products built with focus
the old tiagogalvao.com WordPress homepage with a dark green gradient herothe new tiagogalvao.com Astro homepage with a dark hero and pink accent

This site, the one you are reading right now, is the case study. Every page here used to run on a WordPress theme I built entirely from scratch. It now runs on a statically generated Astro build with structured content and a component system I own end to end.

Goals and Challenges

The old site was fully custom, no page builder, no third-party plugins, every template and interaction hand-built. That gave me complete control, but it also meant every page was assembled by PHP on every single visit, and content lived in a hand-rolled admin setup with no real safety net. A missing or mistyped field would not show up until it rendered wrong on a live page.

I also wanted my own site to be an honest demonstration of how I build for clients today: content with a defined, validated shape instead of loosely typed fields, pages that ship as static files instead of being assembled on every request, and a design system that lives in code rather than being tangled into templates. If I am telling a client their site can be faster and easier to maintain, my own portfolio needed to prove it first.

The constraints were real, not cosmetic. Every existing case study and blog post had to move without losing its content, its images, or its search ranking, and the contact form, category filtering, and blog search all had to keep working through the rebuild.

About page, before and after

the old WordPress about page, including the skills progress bars and embedded contact formthe new Astro about page, including the working style cards and capabilities grid

The Solution

I rebuilt the site on Astro, generating static HTML at build time instead of rendering pages with PHP on every visit. The one deliberate exception is the contact form, which still needs a live server to send an email.

Every case study and blog post is now content with a defined, validated shape, so a missing field or the wrong data type gets caught before it ever reaches the live site instead of after. That single change removed an entire category of mistakes I used to catch by eye.

The page content is authored with a small set of building blocks made specifically for case studies: a before/after slider, an image gallery with a lightbox, captioned images, expandable feature sections, and video embeds. This entry leans on the before/after slider and the expandable sections you can see throughout the page.

the old WordPress work page with a large MY WORK headingthe new Astro portfolio page with a structured project list and tech tags

Design moved from a green-accented dark theme with animated gradients to a calmer, dark-first system with a light mode toggle. Navigation is now a persistent top bar instead of a full-screen overlay menu, which removed an entire interaction pattern the old site needed just to get from one page to another.

the old WordPress blog listing with a two column grid of post cardsthe new Astro blog listing with category filters, a search box, and a featured post

The blog picked up features the old theme never had: category filtering, on-page search, and a consistent article layout with published and updated dates. The contact form kept its project-type selector, rebuilt with a cleaner layout and clearer validation.

the old WordPress contact page with a plain form and reCAPTCHA noticethe new Astro contact page with a project type selector and a cleaner form
Content, rebuilt with structure

Every case study and blog post moved from hand-rolled custom fields into content with a defined, validated shape. Every URL and slug was preserved, so existing search rankings and inbound links kept working through the migration.

Static by default

The site now ships as static HTML wherever possible, with a live server reserved for the one feature that genuinely needs it. There is no per-request rendering standing between a visitor and a page.

Accessible by default

Semantic structure, skip links, visible focus states, and a light and dark theme toggle were built into the base layout from day one, rather than patched onto an existing template.

Navigation is a good example of that shift on its own. Getting from one page to another on the old site meant opening a full-screen menu overlay. Today those same five links live in a persistent top bar, always visible, never a separate screen.

the old WordPress full-screen navigation overlay listing Home, About, Work, Blog, and Contactthe new Astro site with all navigation links always visible in a persistent top bar

Built for Machines, Too

Search engines were never the only audience I designed for. The site is built so that both traditional crawlers and newer AI agents can understand it without guessing.

Structured data is modeled as a single linked graph rather than a pile of isolated snippets on each page. A Person, a professional-service entity, and the site itself are each declared once with a stable identity, and every page, and every article, links back to them instead of repeating the same information. Articles carry their own schema type with published and updated dates, listing pages are typed as collections, and every page publishes its breadcrumb trail as structured data, not just as visible markup on the page.

Alongside that, the site publishes a plain-text summary of itself, llms.txt and a fuller llms-full.txt, declared directly in the page head as an alternate representation of the page. That gives AI agents and LLM-based crawlers a direct, structured answer to “what is this site and what’s on it,” instead of forcing them to parse and interpret rendered HTML the way a search engine would. The sitemap, robots directives, and rich-snippet preview settings are tuned the same way: give any crawler, human-built or agentic, the clearest possible path to accurate, current information about the site.

Measured Results

Talk about performance is cheap, so here are the numbers I actually measured, running Lighthouse against the production build of this site across five different page types.

Desktop

Page Performance Accessibility Best Practices SEO
Home 100 100 100 100
About 100 100 100 100
Contact 100 100 100 100
Portfolio 100 100 100 100
Blog 100 100 100 100
Blog article 100 100 100 100

Mobile

Page Performance Accessibility Best Practices SEO
Home 100 100 100 100
About 100 100 100 100
Contact 100 100 100 100
Portfolio 100 100 100 100
Blog 100 100 100 100
Blog article 100 100 100 100

The lightest pages load with a Largest Contentful Paint around 1.4 to 1.6 seconds, a 0ms Total Blocking Time, and zero Cumulative Layout Shift, at a page weight under 300 KiB. The blog listing and article pages carry heavier photography and still hold a 100 performance score with a fully responsive, non-janky page. That is a hard budget to hit for any page that has to be assembled by a server on every single visit, no matter how well-written that server code is. Shipping static HTML gets most of the way there before any manual optimization work even starts.

Doubt it? Try it for yourself: run this site through PageSpeed Insights.

What This Demonstrates

This migration is a case study in rebuilding a personal brand’s home without losing what already worked: content modeled with real structure, a static-first build with a narrow, deliberate server footprint, a small component library made for case-study writing, and a design system rebuilt from the ground up without losing a single page’s search history. It is also the most honest answer I can give when someone asks what a WordPress-to-Astro migration actually looks like in practice, because they are looking at the result of it right now.