Migrating from Other Web Frameworks to Astro: A Complete Technical Guide
Migrating from React, Vue, Svelte, or Solid to Astro involves scaffolding a new project with your existing UI framework integration, converting routes to Astro's file-based system in src/pages/, moving data fetching into server-side load functions or Content Collections, and replacing deprecated APIs like Astro.resolve with import.meta.glob.
When migrating from other web frameworks to Astro, you are fundamentally shifting from a client-side hydration model to Astro's "islands architecture" that renders HTML on the server by default. This guide walks through the concrete steps required to migrate projects from React, Vue, Svelte, Solid, or similar frameworks into Astro, referencing specific implementation details from the withastro/astro source code.
Scaffold the Project and Configure UI Framework Integration
Start by creating a fresh Astro project using the official CLI:
npm create astro@latest
The default configuration created by the CLI is documented in packages/astro/README.md and packages/astro/src/cli/docs/core/open-docs.ts. After scaffolding, add the integration for whichever UI framework you are migrating from:
| Framework | Integration Package | Install Command |
|---|---|---|
| React | @astrojs/react |
npm install @astrojs/react |
| Vue | @astrojs/vue |
npm install @astrojs/vue |
| Svelte | @astrojs/svelte |
npm install @astrojs/svelte |
| Solid | @astrojs/solid |
npm install @astrojs/solid |
Each integration's specific configuration options are documented in their respective README files under packages/integrations/.
Convert Routes to File-Based Routing
Astro uses a file-based routing system where every file in src/pages/ becomes a route. This replaces framework-specific routers like React Router or Vue Router.
For example, converting a React route /src/pages/home.jsx to Astro:
---
// src/pages/home.astro
import Home from "../components/Home.jsx";
---
<Home />
The underlying Vite plugin that processes these routes is documented in packages/astro/src/vite-plugin-html/README.md. If your previous framework used nested layouts, create an src/layouts/ directory and reference layouts via frontmatter:
---
// src/pages/blog/[slug].astro
import BlogLayout from "../layouts/BlogLayout.astro";
export const layout = BlogLayout;
---
<slot />
Adapt Component Import Models
Astro treats UI components as partial islands that can be imported directly into .astro files. Replace framework-specific entry points with Astro's component-oriented imports:
---
// src/components/Button.astro
import { Button as ReactButton } from "../ui/react/Button.jsx";
---
<ReactButton class="primary">Click me</ReactButton>
For Vue migrations, import .vue files directly:
---
// src/components/Chart.astro
import Chart from "./Chart.vue";
---
<Chart :data={data} />
The Vue integration's import handling is defined in packages/integrations/vue/README.md.
Migrate Data Fetching to Server-Side Logic
Move data-fetching logic from client-side useEffect hooks or component lifecycles into Astro's server-side load functions:
---
// src/pages/posts/[slug].astro
export async function load({ params }) {
const post = await fetch(`https://my.api/posts/${params.slug}`).then(r => r.json());
return { post };
}
---
<h1>{post.title}</h1>
<p>{post.content}</p>
For static content, use the Content Collections API introduced in v4:
// src/content/config.ts
import { defineCollection } from "astro:content";
export const collections = {
blog: defineCollection({ type: "content" })
};
The migration to import.meta.glob is discussed in packages/astro/CHANGELOG-v4.md at line 5341, which states: "The easiest path is to migrate to import.meta.glob". If you previously used Astro.resolve, replace it with import.meta.glob or the Content Collections API.
Configure Static vs. Server Rendering
In astro.config.mjs, specify the default rendering mode:
export default {
output: "static", // default – prerender all pages
// output: "server", // for SSR pages
};
Individual pages can override the global setting:
---
export const prerender = false;
---
The output flag behavior is documented in packages/astro/CHANGELOG-v4.md at line 737.
Select and Configure Deployment Adapters
Astro's adapters replace framework-specific deployment pipelines. For example, migrating from a custom Netlify function:
pnpm add -D @astrojs/netlify
// astro.config.mjs
import netlify from "@astrojs/netlify";
export default {
adapter: netlify(),
};
For Cloudflare Pages migrations, use the Cloudflare Workers adapter. Migration notes live in packages/integrations/cloudflare/CHANGELOG.md at line 112.
Replace Deprecated APIs
| Removed API | New Replacement | Source Reference |
|---|---|---|
Astro.resolve |
import.meta.glob or Content Collections |
packages/astro/CHANGELOG-v2.md line 2019 |
Astro.locals.runtime |
ctx.locals in API routes or adapters |
packages/integrations/cloudflare/CHANGELOG.md line 15075 |
| Pre-Shiki theme names (v0.14) | New theme names | packages/markdown/remark/CHANGELOG.md lines 709-728 |
End-to-End Migration Example
Below is a complete conversion of a React page that fetched data, used a component, and was statically rendered.
Original React (src/pages/User.jsx)
import React from "react";
import Avatar from "../components/Avatar";
export default function User({ id }) {
const [user, setUser] = React.useState(null);
React.useEffect(() => {
fetch(`/api/users/${id}`).then(r => r.json()).then(setUser);
}, [id]);
return user ? (
<div>
<Avatar src={user.avatar} />
<h2>{user.name}</h2>
</div>
) : <p>Loading…</p>;
}
Converted Astro (src/pages/user/[id].astro)
---
// src/pages/user/[id].astro
import Avatar from "../../components/Avatar.jsx";
export async function load({ params }) {
const user = await fetch(`https://my.api/users/${params.id}`).then(r => r.json());
return { user };
}
---
{user ? (
<div>
<Avatar src={user.avatar} />
<h2>{user.name}</h2>
</div>
) : <p>Loading…</p>}
Key changes:
- Data fetching moved to a
loadfunction that runs once at build or request time - The React component is imported and used directly without
useEffectbecause data is already present
Summary
- Scaffold with framework integrations: Use
npm create astro@latestthen add@astrojs/react,@astrojs/vue, or other integrations to reuse your existing components. - Adopt file-based routing: Move all routes to
src/pages/and convert dynamic routes using[param].astrosyntax. - Shift data fetching server-side: Replace client-side
useEffectdata fetching with Astro'sloadfunctions or the Content Collections API. - Configure rendering modes: Set
output: "static"oroutput: "server"inastro.config.mjsand useexport const prerenderfor page-level overrides. - Update deployment adapters: Replace framework-specific deployment logic with official adapters like
@astrojs/netlifyor@astrojs/cloudflare. - Remove deprecated APIs: Replace
Astro.resolvewithimport.meta.glob, updateAstro.locals.runtimetoctx.locals, and verify Shiki theme names.
Frequently Asked Questions
How do I migrate client-side data fetching to Astro?
Move your data fetching logic from React's useEffect or Vue's onMounted into Astro's server-side load function. This function runs before the component renders, allowing you to fetch data from APIs or databases and pass it as props to your components. For static sites, this happens at build time; for SSR sites, it runs on each request.
Can I keep using my existing React or Vue components during migration?
Yes. Astro's "Bring your own UI framework" philosophy allows you to import React, Vue, Svelte, or Solid components directly into .astro files. Install the appropriate integration (e.g., @astrojs/react), import your component, and Astro will render it as an interactive island only when needed, while keeping the surrounding page as static HTML.
What replaces Astro.resolve when migrating from older Astro versions?
Replace Astro.resolve with import.meta.glob or migrate to the Content Collections API introduced in Astro v4. According to packages/astro/CHANGELOG-v4.md, import.meta.glob is the recommended path for dynamically importing files, while Content Collections provide type-safe content management for blogs, documentation, and marketing sites.
How do I choose between static and server output during migration?
Set the default rendering mode in astro.config.mjs using output: "static" (default, prerendered at build time) or output: "server" (SSR on each request). Individual pages can override this default by exporting prerender = false for server-rendered pages within a static site, or prerender = true for static pages within a server site. Choose based on whether your content changes frequently (use server) or can be built ahead of time (use static).
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →