Moving House to Astro

May 30, 2026 • @aethery
Moving House to Astro

Hey, it’s him again…

Just last week, he finished migrating from styled-components to Tailwind CSS. He thought the digital house was stable, but when he sat down and looked at the package.json manifest, he realized an uncomfortable truth: Gatsby had officially entered “maintenance mode” after Netlify acquired and then abandoned it. Over 1,500 dependencies sat idle in node_modules, many packages no longer receiving updates, and every yarn install was a minute and a half of pointless waiting

He decided: instead of patching a sinking foundation, just rebuild from the ground up

Why Astro

He weighed Next.js, Remix, and Astro. For a purely static Markdown blog, the answer was clear:

  • Zero JavaScript by default: Astro ships not a single byte of JS to the browser unless explicitly asked. Gatsby always bundled the React runtime (~40KB gzipped) even when pages only displayed static content
  • Content Collections: Instead of writing complex GraphQL queries in gatsby-node.ts and passing data through pageContext, Astro provides getCollection() with Zod schema validation, type-safe from end to end
  • Lightweight ecosystem: Gatsby needed 10+ plugins just to handle images, sitemap, and markdown. Astro handles everything with sharp and a few lines of config
  • Build time: This was the deciding factor

The teardown

The migration took one weekend afternoon. The process was straightforward:

Step 1: Clear the foundation

Delete all Gatsby config files (gatsby-config.ts, gatsby-node.ts, gatsby-browser.tsx, postcss.config.js, tailwind.config.js) and replace with a single file: astro.config.mjs

Step 2: Move the content

Move content/blog/ into src/content/blog/ so Astro can optimize images at build time. Create content.config.ts with Zod schema to replace GraphQL

Step 3: Convert components

Each React TSX file was rewritten as an Astro component. No useState, no useEffect, no hooks whatsoever. Layout.tsx + SEO.tsx merged into a single Layout.astro. NavBar, Footer, Card, all became static templates requiring no runtime

Step 4: Upgrade Tailwind

Tailwind v3 to v4: @tailwind directives became @import "tailwindcss", tailwind.config.js became a CSS @theme block. Configuration lives in CSS, no separate JavaScript file needed

Two unexpected issues

No migration is perfectly smooth

Duplicate Content IDs: Many posts had both index.md (Vietnamese) and index.en.md (English) in the same directory. Astro’s glob loader generated identical IDs. Solution: write a custom generateId function in the content config to include the filename in the ID

Missing images from Notion: Two legacy posts migrated from Notion referenced ./images/ directories that never existed. Gatsby silently rendered broken <img> tags, but Astro is stricter: it refuses to build. Solution: write a small remark plugin (remark-strip-missing-images.mjs) to automatically remove references to non-existent images

Results

Gatsby 5Astro 5
Dependencies~1,500 packages~330 packages
Build time (local, cached)7-47 seconds1.6 seconds
Build time (Cloudflare CI)~3 minutes22 seconds
JS shipped to browserReact runtime (~40KB)0 KB
Output size~65MB44MB
Pages generated122122

122 pages, 58 Vietnamese posts, 58 English posts, 6 static pages, sitemap, all preserved. Not a single URL was lost

The most notable result: on Cloudflare Pages, total time from code push to deploy completion is now about 59 seconds (including clone, install, build, and deploy). Previously with Gatsby, the build step alone consumed over 2 minutes

Technical takeaways

  • When a core framework enters maintenance mode, don’t wait. Migration cost only increases over time, it never decreases
  • For static content sites (blogs, documentation), shipping a JavaScript runtime to the browser is unnecessary waste. Astro eliminates this problem entirely
  • Astro’s Content Collections with Zod schema validation is a major improvement over Gatsby’s GraphQL layer. Type-safe, simple, no need to learn an extra query language
  • Always check edge cases when migrating: duplicate filenames, missing images, changed paths. Write scripts or small plugins to handle them automatically instead of fixing files by hand

The digital house is now lighter, faster, and far simpler. Sometimes, the best way to optimize a system is not to add more, but to remove what’s unnecessary

❤️ cowriter aethery