How to migrate your website from Next.js to Astro

Lokman Musliu Founder and CEO of Lucky Media
Lokman Musliu

September 22, 2025 · 5 min read

How to migrate your website from Next.js to Astro

So you’ve got aNext.js marketing site running on the shiny App Router, and now you’re hearing everyone rave about Astro as if it’s the espresso shot of the frontend world: lightweight, fast, and (most importantly) with very little JavaScript hangover for your users.

But how do you get from React Server Components to Astro’s chill “islands” vibe without losing your sanity?

That’s exactly what we’re about to tackle. This post is a detailed, code-heavy, engineer-first guide to migrating from Next.js App Router to Astro.

Why Migrate to Astro?

Imagine you have a Ferrari (Next.js), but all you do is buy groceries across the street. Sure, it’s powerful, but maybe you don’t need all that engine for a marketing site.

Astro flips that idea on its head. It shines for content-heavy sites, delivering essentially zero JavaScript by default. And when you do need interactivity, islands let you sprinkle in React, Vue, Svelte, Solid, even jQuery if you’re feeling chaotic (please don’t).

Key reasons why developers migrate:

  • Performance: Astro ships less JS by default than Next.js.

  • Flexibility: Use multiple UI frameworks in one project.

  • Simplicity: File-based routing, scoped styles, no client bundle bloat unless you need it.

Think of Astro as an efficient city bike instead of that Ferrari, built for smooth riding through marketing sites, blogs, and docs.

Astro vs Next.js comparison

Routing: From App Router to Astro Pages

Routing is often the first challenge in migrations. Both frameworks use file system-based routing, but the details differ.

Static Route

Next.js:

// app/about/page.js
export default function AboutPage() {
  return <h1>About Us</h1>;
}

Astro:

---
// src/pages/about.astro
---
<html>
  <body>
    <h1>About Us</h1>
  </body>
</html>

Notice, Astro pages are literally.astro files inside src/pages. No special page.js distinction.

Dynamic Route

Both use [slug] style conventions:

Next.js:

// app/products/[slug]/page.js
export default function ProductDetailPage({ params }) {
  return <h1>Product: {params.slug}</h1>;
}

Astro:

---
// src/pages/products/[slug].astro
const { slug } = Astro.params;
---
<h1>Product: {slug}</h1>

Pretty similar, but no React props here. Astro exposes Astro.params.

Nested Layouts

Next.js:

// app/dashboard/layout.js
export default function Layout({ children }) {
  return <section>{children}</section>;
}

Astro:

In Astro, layouts are just components with <slot /> for child content.

---
// src/layouts/BaseLayout.astro
---
<html>
  <body>
    <nav>NavBar!</nav>
    <slot />
  </body>
</html>

Then use in pages:

---
// src/pages/dashboard.astro
import BaseLayout from "../layouts/BaseLayout.astro";
---
<BaseLayout>
  <h1>Dashboard</h1>
</BaseLayout>

Summary of Routing Differences

Feature

Next.js App Router

Astro

Pages files

page.js in folders

.astro in src/pages/

Params

props.params

Astro.params

Layouts

layout.js + nested by default

Explicit <slot /> composition

Astro uses zero Javascript by default

Components: RSC vs Islands

Here is the biggest conceptual leap.

  • Next.js App Router: Server Components by default, fallback to use client for browser code.

  • Astro: Zero JS by default, you opt in to hydration with client:* directives.

Next.js Example

// app/dashboard/page.js
import HeavyChart from "../components/ClientChart";
import { fetchData } from "../utils/lib";

export default async function DashboardPage() {
  const data = await fetchData();
  return <HeavyChart data={data} />;
}

Astro Island Example

---
// src/pages/index.astro
import HeavyChart from "../components/HeavyChart.jsx";
---

<HeavyChart client:load />

And HeavyChart.js is a normal React file.

The client:load, client:visible, client:idle directives give you important performance control.

Data Fetching Differences

Astro encourages server/build-time fetching, while Next.js encourages in-component data fetching.

Next.js:

// app/products/page.js
export default async function Products() {
  const products = await getProducts(); 
  return <ul>{products.map(p => <li>{p.name}</li>)}</ul>;
}

Astro:

---
// src/pages/products.astro
const products = await fetch("https://api.example.com/products").then(r => r.json());
---
<ul>
  {products.map(p => <li>{p.name}</li>)}
</ul>

Astro doesn’t ship a client runtime for this; it’s statically compiled.

Astro works with React Vue Preact Svelte

Styling

Both support all the usual suspects: CSS modules, Tailwind, etc.

But Astro makes scoping the default inside .astro components.

<button>Click Me</button>

<style>
  button {
    background: blue;
  }
</style>

In Next.js, that’s only true if you use .module.css.

Image Optimization

  • Next.js: built-in.

  • Astro: built-in.

---
import { Image } from 'astro:assets';
---

// Image stored in public/images/
<Image src="/images/stars.png" alt="A rocketship in space." />

// Remote image on another server
<Image src="https://example.com/images/remote-image.png" />

Almost identical developer experience.

APIs: Server Functions vs. Routes

Next.js has Server Actions. Handy but tied to React.

Astro uses classic file-based API routes.

// src/pages/api/contact.ts

export const POST: APIRoute = async ({ request }) => {
  const { name } = await request.json();

  return new Response(JSON.stringify({ hi: name }));
};

This disconnection from the component layer can feel “old school”, yet less coupled.

Middleware

Both frameworks let you slip in request middleware:

// src/middleware.ts
import { defineMiddleware } from "astro:middleware";

export const onRequest = defineMiddleware( (ctx, next) => {
    if (! ctx.cookies.get("auth")) {
        return Response.redirect("/login")
    }

    return next();
});

Clean, Node-standard APIs.

Astro Partner Agency

SEO

Next.js: Fancy metadata fields & generateMetadata.

Astro: Just HTML <head> or reusable layout props.

---
// src/layouts/Layout.astro
const { title, desc } = Astro.props;
---
<html>
  <head>
    <title>{title}</title>
    <meta name="description" content={desc} />
  </head>
  <body>
	<slot />
  </body>
</html>

You can also add SEO defaults here and overwrite them as needed.

Rendering: SSG + SSR

Next.js blends SSG and RSC. Astro is SSG first, with optional SSR by flipping output: 'server' in config, and a hybrid mode to combine static pages with SSR.

Errors & Loading States

Next.js:

  • loading.js file for spinners.

  • error.js for boundaries.

Astro:

  • No conventions. Use a simple loading island or custom 500.astro.

This may feel like more manual work, but it gives you freedom without React coupling.

  • Next.js: useRouter from next/navigation.

  • Astro: No SPA router by default. Use native <a> links, or enable View Transitions for smooth SPA-like behavior.

<a href="/about">Smooth SPA-like navigation!</a>

And you’re done.

Step-by-step migration path

  1. Start with static pages: Move all Next.js page.js simple content into src/pages/*.astro.

  2. Identify interactive areas: Port those components into islands (client:*).

  3. Swap layouts: Replace React layouts with Astro .astro layout components + <slot />.

  4. Rewire data fetching: Anything in fetch() moves to the top of .astro.

  5. Replace Server Actions: Create /api routes.

  6. SEO review: Make sure <meta> values are preserved.

astro vs next.js feature comparison

Conclusion

Migrating from Next.js App Router to Astro is like switching from a Swiss Army knife to a scalpel. You lose some built-in abstractions (nested layouts, convention-based loading/error components, Server Actions), but you gain simplicity, performance, and flexibility.

If your site is focused on marketing or content, and not a fully interactive dashboard SPA, Astro is the best choice. Use Astro’s multi-framework islands to hang onto React components you already wrote.

Bottom line: fewer megabytes, happier users, fewer hydration headaches.


Let's chat!

If you need help migrating a Next website to Astro, let's get in touch.

Lucky Media is proud to be recognized as a leading software development agency!

FAQs

Can I still use React components in Astro?

Astro supports React (and many frameworks) via the @astrojs/react integration. Just mark them with client:load or similar if you need hydration.

Do I lose API routes moving to Astro?

Nope. In fact, Astro’s /api directory handles this in a nice, decoupled way. You just write GET/POST exports in .ts or .js files.

How does Astro handle authentication flows?

Astro provides middleware (src/middleware.ts) where you can check cookies, sessions, and JWTs. Combine this with API routes or SSR for protected routes.

What about ISR (Incremental Static Regeneration) like in Next.js?

Astro doesn’t have ISR natively. Instead, you’d set server mode + cache headers or re-run builds via your CI/CD pipeline for fresh content. For most marketing sites, this simpler model is enough.

Is migration worth it performance-wise?

Short answer: If your site is mostly static content, YES. Astro averages smaller bundle sizes and better Core Web Vitals scores because islands avoid overshipping JS. If you’re building a complex authenticated app with websockets and tons of client state? Stick with Next.js.

Technologies

Next.jsAstro
Lokman Musliu Founder and CEO of Lucky Media
Lokman Musliu

Founder and CEO of Lucky Media

Stay up-to-date

Be updated with all news, products and tips we share!

Let’s chat

We partner with a limited number of brands each quarter to ensure senior-level attention on every project.

lokman and arlind headshots
Teamwork