For years, Gatsby was the gold standard for the JAMstack. But as the web evolved, the “heavy hydration” problem became a bottleneck. In my recent projects, I’ve noticed a recurring theme: developers are exhausted by Gatsby’s build times and the massive JavaScript bundles sent to the browser. This is why I’ve been spending more time exploring Astro. If you’re wondering how to migrate Gatsby to Astro, you’re likely looking for better performance and a simpler developer experience.
The transition isn’t a one-click process, but it’s surprisingly manageable because Astro allows you to bring your existing React components with you. Whether you’re questioning if JAMstack is still worth it in 2026 or simply want to optimize your Lighthouse scores, this guide will walk you through the migration path I’ve refined over several production sites.
Prerequisites
Before we dive into the code, make sure you have the following ready:
- A running Gatsby site with a known content source (Markdown, Contentful, Sanity, etc.).
- Node.js (LTS version) installed on your machine.
- Basic familiarity with the terminal and React (since you’re coming from Gatsby).
- A clear inventory of your Gatsby plugins, as not all of them have 1:1 Astro equivalents.
Step 1: Initialize Your Astro Project
Don’t try to convert your Gatsby folder in place. Start fresh to avoid dependency hell. I recommend using the Astro CLI to scaffold a clean environment.
npm create astro@latest
During the setup, choose the “Empty” template. This gives you full control over the structure. Once installed, add the React integration since most of your Gatsby components are likely written in React:
npx astro add react
Step 2: Mapping Your Content Source
Gatsby uses a GraphQL data layer, which is often the most complex part of the migration. Astro takes a different approach: it fetches data directly using standard fetch() or dedicated SDKs in the frontmatter.
If you use Markdown/MDX:
Move your content/ folder from Gatsby to Astro’s src/content/ directory. Use Astro’s Content Collections to define your schema. It provides the same type-safety as Gatsby’s GraphQL but without the overhead.
// src/content/config.ts
import { defineCollection, z } from 'astro:content';
const blog = defineCollection({
type: 'content',
schema: z.object({
title: z.string(),
pubDate: z.date(),
description: z.string(),
}),
});
export const collections = { blog };
If you use a Headless CMS:
Replace your gatsby-source-xxx plugins with the CMS’s own JavaScript SDK. Instead of querying GraphQL in a template, you’ll fetch the data directly in the Astro component’s script section (the “code fence”).
Step 3: Migrating Components and Logic
This is where Astro shines. You don’t have to rewrite your UI from scratch. You can move your React components into src/components/ and use them directly.
However, remember the Astro Islands architecture. By default, components are rendered as static HTML. If your Gatsby component relies on useEffect or useState, you must tell Astro to hydrate it on the client:
---
import MyReactComponent from '../components/MyReactComponent';
---
In my experience, moving to a “zero-JS by default” mindset is the hardest but most rewarding part of the migration. If you’re weighing this against other frameworks, you might find our comparison of Astro vs Next.js for static sites useful.
Step 4: Handling Routing and Redirects
Gatsby uses a file-based routing system similar to Astro, but there are differences in how dynamic routes are handled. In Gatsby, you’d use gatsby-node.js to create pages. In Astro, you use getStaticPaths().
Gatsby approach (gatsby-node.js):
createPages({ pages: ... })
Astro approach (src/pages/blog/[slug].astro):
---
export async function getStaticPaths() {
const posts = await getCollection('blog');
return posts.map(post => ({ params: { slug: post.slug } }));
}
const { post } = Astro.props;
---
To maintain your SEO, ensure your URL structures remain identical. If you must change a URL, use an astro.config.mjs redirect or a _redirects file if you are deploying Astro to GitHub Pages or Netlify.
Pro Tips for a Smoother Migration
- Audit your plugins: Many Gatsby plugins (like
gatsby-plugin-image) are handled natively by Astro’s<Image />component. Don’t look for a plugin; look for a built-in feature. - CSS Strategy: If you used CSS Modules in Gatsby, they work out of the box in Astro. If you used Styled Components, keep in mind that they require client-side hydration.
- Incremental Migration: If your site is massive, consider a reverse proxy (like Cloudflare Workers) to serve some pages from Gatsby and new pages from Astro during the transition.
Troubleshooting Common Issues
Issue: “Window is not defined” errors in React components.
Fix: This happens because Astro renders components on the server first. Wrap browser-specific logic (like localStorage) inside a useEffect hook or use the client:only="react" directive.
Issue: Build times are still slow.
Fix: Check your image sizes. While Astro is fast, processing 1,000+ unoptimized images can still lag the build. Leverage Astro’s remote image optimization.
What’s Next?
Now that you’ve migrated, it’s time to optimize. I recommend auditing your site with PageSpeed Insights. You’ll likely see a dramatic jump in your “First Contentful Paint” (FCP) and “Total Blocking Time” (TBT) because you’ve stripped away the Gatsby hydration overhead.
If you’re looking for more ways to automate your workflow or explore new tools, check out my other guides on technical productivity.