| name | migrate |
| description | Guide migration to Astro from other frameworks or between Astro versions. Use when converting Next.js, Nuxt, Gatsby projects or upgrading Astro. |
Migrate to Astro
When to Use
- Migrating from Next.js, Nuxt, Gatsby, or other frameworks
- Upgrading between major Astro versions
- Converting a static HTML site to Astro
- Moving from Create React App to Astro
Instructions
1. Identify Migration Path
| From | Complexity | Key Changes |
|---|
| Static HTML | Low | Wrap in .astro, add layouts |
| Next.js (Pages) | Medium | Route structure similar, convert JSX |
| Next.js (App) | Medium-High | Server components map well to Astro |
| Nuxt | Medium | Vue components work with integration |
| Gatsby | Medium | GraphQL → Content Collections |
| Create React App | Medium | Add routing, convert to islands |
| Astro v4 → v5 | Low-Medium | Content Layer changes begin |
| Astro v5 → v6 | Medium | Content collections, Astro.glob(), and runtime changes |
2. Project Setup
Create new Astro project alongside existing:
npm create astro@latest my-astro-site
Or convert in-place by installing Astro:
npm install astro
Update package.json scripts:
{
"scripts": {
"dev": "astro dev",
"build": "astro build",
"preview": "astro preview"
}
}
If upgrading to Astro 6, verify the runtime first:
node --version
Astro 6 requires Node 22.12.0 or newer. Also upgrade official integrations and adapters alongside Astro to avoid version skew.
3. Migration Patterns
Static HTML → Astro
- Rename
.html to .astro
- Move to
src/pages/
- Extract repeated sections to
src/layouts/
- Convert to components as needed
---
// src/layouts/Layout.astro
const { title } = Astro.props
---
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>{title}</title>
</head>
<body>
<slot />
</body>
</html>
Next.js → Astro
Pages Router:
| Next.js | Astro |
|---|
pages/index.js | src/pages/index.astro |
pages/blog/[slug].js | src/pages/blog/[slug].astro |
getStaticProps | Frontmatter code block |
getStaticPaths | getStaticPaths() export |
_app.js | src/layouts/Layout.astro |
next/image | astro:assets Image component |
next/link | Standard <a> tags |
Convert a Next.js page:
export async function getStaticPaths() {
const posts = await getPosts()
return { paths: posts.map(p => ({ params: { slug: p.slug } })) }
}
export async function getStaticProps({ params }) {
const post = await getPost(params.slug)
return { props: { post } }
}
export default function Post({ post }) {
return <article><h1>{post.title}</h1></article>
}
---
// Astro: src/pages/blog/[slug].astro
import { getCollection, render } from 'astro:content'
export async function getStaticPaths() {
const posts = await getCollection('blog')
return posts.map(post => ({
params: { slug: post.id },
props: { post },
}))
}
const { post } = Astro.props
const { Content } = await render(post)
---
<article>
<h1>{post.data.title}</h1>
<Content />
</article>
Keep React components:
npx astro add react
Then use with client directives:
---
import InteractiveWidget from '../components/InteractiveWidget.tsx'
---
<InteractiveWidget client:visible />
Nuxt → Astro
npx astro add vue
| Nuxt | Astro |
|---|
pages/ | src/pages/ (similar) |
components/ | src/components/ |
layouts/ | src/layouts/ |
nuxt.config.ts | astro.config.mjs |
| Auto-imports | Explicit imports |
Vue components work directly:
---
import MyVueComponent from '../components/MyVueComponent.vue'
---
<MyVueComponent client:load />
Gatsby → Astro
| Gatsby | Astro |
|---|
| GraphQL queries | Content Collections |
gatsby-image | astro:assets Image |
gatsby-plugin-* | Astro integrations |
| MDX pages | MDX in content collections |
Convert GraphQL to Content Collections:
import { defineCollection } from 'astro:content'
import { glob } from 'astro/loaders'
import { z } from 'astro/zod'
const blog = defineCollection({
loader: glob({ base: './src/content/blog', pattern: '**/*.{md,mdx}' }),
schema: z.object({
title: z.string(),
date: z.coerce.date(),
}),
})
export const collections = { blog }
4. Astro Version Upgrades
Upgrade command:
npx @astrojs/upgrade
v5 → v6 Key Changes:
- Node 22.12.0+ is required
- Legacy content collections support is removed
src/content/config.ts moves to src/content.config.ts
- Each collection now needs a
loader
z should come from astro/zod
entry.id replaces legacy slug usage in collection routing
entry.render() becomes render(entry)
Astro.glob() is removed in favor of import.meta.glob() or getCollection()
<ViewTransitions /> is removed; use <ClientRouter />
Astro.site inside getStaticPaths() should become import.meta.env.SITE
Astro 6 upgrade checklist:
- Upgrade Astro and official integrations/adapters together
- Verify Node 22.12.0+ locally and in CI/deployment
- Move
src/content/config.ts to src/content.config.ts
- Add a
loader to every collection
- Replace
import { defineCollection, z } from 'astro:content' with import { defineCollection } from 'astro:content' and import { z } from 'astro/zod'
- Replace
post.slug with post.id
- Replace
post.render() with render(post)
- Replace
Astro.glob() with import.meta.glob() or getCollection()
- Replace
<ViewTransitions /> with <ClientRouter />
Check migration guide: docs.astro.build/guides/upgrade-to/v6
5. Common Migration Tasks
Move static assets
public/images/ → public/images/ (same)
src/assets/ → src/assets/ (processed by Vite)
Update image imports
---
import { Image } from 'astro:assets'
import heroImage from '../assets/hero.jpg'
---
<Image src={heroImage} alt="Hero" width={800} height={400} />
Convert CSS
- Global CSS → Import in layout
- CSS Modules → Work as-is
- Styled Components → Use with React integration
- Tailwind →
npx astro add tailwind
Environment variables
// Next.js
NEXT_PUBLIC_API_URL
// Astro
PUBLIC_API_URL // Client-side
API_SECRET // Server-side only
Replace Astro.glob()
---
// Before
const posts = await Astro.glob('./posts/*.md')
// After
const posts = Object.values(import.meta.glob('./posts/*.md', { eager: true }))
---
For content collections, prefer getCollection() instead of raw globs.
6. Verification Checklist
Resources