DEV Community

Cover image for Why I Ditched Next.js and Rebuilt My Site with Astro
Waseem raja Shaik
Waseem raja Shaik

Posted on • Originally published at waseemrajashaik.me

Why I Ditched Next.js and Rebuilt My Site with Astro

Sometimes the best code is the code you don't ship to the browser.

// package.json diff
- "next": "15.3.5",
- "react": "19.0.0",
- "framer-motion": "12.23.0",
- "gsap": "3.13.0",
+ "astro": "6.x",
+ // that's... kind of it
Enter fullscreen mode Exit fullscreen mode

Let me be clear upfront: I still love Next.js. I spent months building my v1 portfolio with it. React 19, Tailwind v4, Framer Motion, GSAP, dual design modes with smooth morphing animations, the whole nine yards. It was a flex. It looked great. I was proud of it.

But then I asked myself a simple question: "What am I actually building here?"

The Realization

My v1 site was a portfolio. It had an about section, experience timeline, skills grid, project cards, a writing section, and social links, all on one page with fancy animations. The blog was there, but it was secondary.

When I decided to pivot to weekly blogging, I looked at my Next.js setup and thought:

Framework JS shipped to browser: ~180KB
React runtime: ~45KB
Framer Motion: ~60KB
GSAP: ~30KB
Content that actually changes: some markdown text

πŸ€” Something doesn't add up.
Enter fullscreen mode Exit fullscreen mode

I was shipping a full React runtime, animation libraries, and client-side hydration... to render static text that never changes. Every single blog post was being hydrated on the client, running JavaScript, setting up event listeners, for content that's literally just paragraphs and code blocks.

That's when it hit me: I was using a chainsaw to butter toast.

Why Not Just Keep Next.js?

Fair question. Next.js can do static sites. It has SSG, ISR, the whole deal. But here's what bugged me:

1. The bundle tax

Even with static generation, Next.js ships React to the browser. Every page gets hydrated. For a blog post that's just text and code blocks, that's unnecessary JavaScript that the user's browser has to download, parse, and execute.

2. The complexity overhead

My v1 had content-collections, next-mdx-remote, react-markdown, @wooorm/starry-night for syntax highlighting, remark-gfm, rehype... the dependency tree for just rendering markdown was wild.

// v1: Dependencies just for blog content
"@content-collections/core": "0.10.0",
"@content-collections/mdx": "0.2.2",
"@content-collections/next": "0.2.6",
"next-mdx-remote": "5.0.0",
"react-markdown": "10.1.0",
"remark-gfm": "4.0.1",
"rehype": "13.0.2",
"@mdx-js/react": "3.1.0",
"@wooorm/starry-night": "3.8.0",
"hast-util-to-html": "9.0.5"
Enter fullscreen mode Exit fullscreen mode

3. The "everything is a component" trap

In React-land, even a simple blog post layout becomes a tree of components with state, effects, and client directives. My blog-post-layout.tsx was a client component importing react-markdown with custom renderers, wrapped in motion divs. For what? To display an article.

Enter Astro

I'd been hearing about Astro for a while. The pitch was simple: ship zero JavaScript by default. Only hydrate what actually needs interactivity. For a blog, that's... almost nothing.

Here's what sold me:

1. Zero JS by default

Astro renders everything to HTML at build time. No React runtime shipped. No hydration. A blog post page literally sends HTML and CSS to the browser. That's it. The way the web was meant to work.

v1 (Next.js) blog post page: ~230KB JS
v2 (Astro) blog post page: ~0KB JS (just HTML + CSS)
Enter fullscreen mode Exit fullscreen mode

Yes, zero JavaScript on a blog post page. The only JS on the entire site is a tiny inline script for the dark mode toggle.

2. Built-in content collections

Astro has content collections as a first-class feature. No third-party packages needed. Define a schema, drop MDX files in a folder, and you're done.

// That's the entire content setup
const blog = defineCollection({
  loader: glob({ pattern: '**/*.mdx', base: './src/content/blog' }),
  schema: z.object({
    title: z.string(),
    description: z.string(),
    date: z.coerce.date(),
    tags: z.array(z.string()),
    published: z.boolean().default(false),
  }),
});
Enter fullscreen mode Exit fullscreen mode

Compare that to the three packages and custom transform functions I needed in Next.js.

3. Shiki built-in

Syntax highlighting just works. No installing starry-night or prism or configuring rehype plugins. Astro uses Shiki out of the box with dual theme support:

// astro.config.mjs
markdown: {
  shikiConfig: {
    themes: {
      light: 'github-light',
      dark: 'github-dark',
    },
  },
},
Enter fullscreen mode Exit fullscreen mode

That's it. Every code block in every blog post gets beautiful syntax highlighting with zero client-side JavaScript.

4. I can still use React (when I need it)

This was crucial. Astro's "islands" architecture means I can drop in a React component when I actually need client-side interactivity. Everything else is static HTML.

<!-- Only this component ships JS, everything else is static -->
<InteractiveWidget client:load />

<!-- The rest of the page is zero-JS HTML -->
Enter fullscreen mode Exit fullscreen mode

For my site, I ended up handling tag filtering and the sidebar toggle with small inline scripts instead of React. That's even less JavaScript shipped.

5. View Transitions API

Astro has built-in support for the View Transitions API. Smooth page-to-page animations with zero JavaScript bundle cost. No Framer Motion. No GSAP. Just native browser APIs.

The Migration

The actual migration was surprisingly smooth:

  • MDX posts: Copied all the files over. The frontmatter schema was almost identical, just had to remove some content-body --- separators that MDX interpreted as frontmatter delimiters.
  • Data: Extracted experience and skills data into simple TypeScript files.
  • Styling: Kept Tailwind v4, same CSS variables, same color scheme.
  • Dark mode: One inline script in the head. No next-themes package.

The whole thing took a day. The v1 had 15+ components and multiple npm packages for content. The v2 has Astro components (basically HTML with props) and barely any dependencies.

The Numbers

Next.js v1 Astro v2
Dependencies 30+ ~10
JS shipped (blog post) ~230KB ~0KB
Build time ~15s ~5s
Lighthouse Performance 85-90 99-100
Framework complexity High Low

What I Miss

I'll be honest, there are things I miss:

  • The morphing layout was genuinely cool. Switching between "original" and "swiss" design modes with smooth Framer Motion animations was a showpiece. But it was a portfolio flex, not a blogging feature.
  • React's component model is more powerful for complex UI. Astro components are simpler but that's by design.
  • The ecosystem: React has a package for everything. Astro's ecosystem is smaller (but growing fast).

The Takeaway

The best framework is the one that fits your use case. Next.js is incredible for web applications: dashboards, e-commerce, SaaS products, anything with lots of interactivity and dynamic data. I'd pick it again in a heartbeat for those.

But for a content-first blog where the primary job is serving static text with good typography and fast page loads? Astro is purpose-built for exactly that.

if (site === 'blog') {
  return 'astro';
} else if (site === 'app') {
  return 'nextjs';
} else {
  return 'it dependsβ„’';
}
Enter fullscreen mode Exit fullscreen mode

The v1 portfolio is still alive as an archive, a reminder that sometimes the best engineering decision is knowing when to use a simpler tool.

Now if you'll excuse me, I have a blog to write every week. And this time, the framework won't be in my way.

Top comments (31)

Collapse
 
alptekin profile image
alptekin I. • Edited

interesting. I heard about astro but to be honest could not spare time to learn what it is actually. I build my web site with next.js too and my main motivation is writing blogs, though there is none, yet. So i might reconsider my tech stack, later.
thanks for the post

Collapse
 
waseemrajashaik profile image
Waseem raja Shaik

Next.js works great for blogging too, so no rush to switch. The main difference I noticed was how much simpler the setup becomes with Astro for content-heavy sites. But honestly, the most important thing is just starting to write. The framework matters less than the habit. Good luck with your first post!

Collapse
 
alptekin profile image
alptekin I.

Exactly, what is what i think truly. Probably, i will stick on this stack for a while, but it is good to know about Astro. Thank you, I am collecting ideas and thoughts currently, but it is already time to turn it to real action :). good luck with your blogs

Thread Thread
 
waseemrajashaik profile image
Waseem raja Shaik

Good luck with yours too! The hardest part is writing the first one. Once that's out, the rest gets easier. Looking forward to reading it when you publish.

Collapse
 
trinhcuong-ast profile image
Kai Alder

"Using a chainsaw to butter toast" -- I'm stealing that line lol.

Made a similar switch for a docs site I maintain. The dependency list alone was reason enough. We had like 8 packages just for markdown rendering and syntax highlighting, and half of them had breaking changes every few months. With Astro it's basically built in.

The View Transitions API support is underrated too. I was using Framer Motion for page transitions and it added ~60KB to every page. Now I get smoother transitions with literally zero JS cost. The browser just handles it natively.

One gotcha I hit during migration: if you're using any React context providers that wrap your whole app (theme, auth, etc), you'll need to rethink that pattern since Astro doesn't have a persistent app shell. Ended up using a combination of cookies and tiny inline scripts for theme state. Actually cleaner than the React approach was.

Collapse
 
waseemrajashaik profile image
Waseem raja Shaik

Ha, feel free to use it! Good point about the context providers gotcha. I hit the same thing with theme state. Ended up with a small inline script in the head that reads localStorage before paint, which actually prevents the flash of wrong theme better than React context ever did. Simpler and faster.

Collapse
 
itskondrat profile image
Mykola Kondratiuk

went through something similar rebuilding a landing page. had Next.js with all the trimmings - ISR, API routes, the works - for what was basically 4 static sections and a contact form. the bundle size was embarrassing. switched to Astro and the lighthouse score jumped before I even touched the CSS. the thing that got me was "what am I actually building here" - that question alone is worth asking before you pick a framework. most sites don't need a runtime

Collapse
 
waseemrajashaik profile image
Waseem raja Shaik

"Most sites don't need a runtime" - that's the whole argument in one line. We reach for full frameworks by default when the content should dictate the tool. Glad the switch worked out for you too.

Collapse
 
itskondrat profile image
Mykola Kondratiuk

exactly. and the framework defaults keep creeping up - once you use one feature you start using three more because they're right there. Astro's constraint feels limiting at first but that's kind of the point

Thread Thread
 
waseemrajashaik profile image
Waseem raja Shaik

Constraints breed focus. When the framework doesn't offer a feature, you ask yourself if you actually need it. Most of the time, the answer is no.

Thread Thread
 
itskondrat profile image
Mykola Kondratiuk

exactly. the constraint forces the decision. most feature sprawl happens because adding something costs nothing in the moment - you only pay later when you are debugging a system with 40 things it can do but nobody remembers why.

Collapse
 
rickcogley profile image
Rick Cogley

Are you hosting on Cloudflare and if so did you find any advantage to using Astro there?

Collapse
 
waseemrajashaik profile image
Waseem raja Shaik

Hosting on Vercel with domain on Porkbun. Deployments are incredibly fast, under a minute from push to live. Astro's static output means Vercel has very little to do at build time compared to Next.js, which had to handle SSR, hydration bundles, and route optimization. Haven't tried Cloudflare Pages but heard good things for static sites.

Collapse
 
rickcogley profile image
Rick Cogley

Cloudflare acquired Astro (blog.cloudflare.com/astro-joins-cl...) so I was curious if they have integrated in any way. By the way Pages is slowly getting morphed into Workers on Cloudflare, so if you try it, start with Workers. The observability log/trace is much better than what you get with Pages.

Thread Thread
 
waseemrajashaik profile image
Waseem raja Shaik

That's a great point about Cloudflare acquiring Astro. I haven't explored Workers yet but the tighter integration between Astro and Cloudflare's edge network sounds promising, especially for static-first sites. Will definitely look into Workers when I consider moving off Vercel. Thanks for the tip on observability, that's useful to know.

Thread Thread
 
rickcogley profile image
Rick Cogley

I wrote about migrating to workers so check my profile when you’re ready.

Thread Thread
 
waseemrajashaik profile image
Waseem raja Shaik

I will look into it. Thanks for suggestion.

Collapse
 
nimrodkra profile image
Nimrod Kramer

love this kind of honest framework comparison. the question "what am I actually building here?" is exactly what millions of developers working on similar content sites should ask themselves. i keep seeing similar discussions on daily.dev about when to use full spa frameworks vs static first approaches. your performance numbers really drive home the point that sometimes the best engineering decision is just using less tech.

Collapse
 
waseemrajashaik profile image
Waseem raja Shaik

Appreciate it. "Using less tech" sounds simple but it's genuinely hard to do when the industry constantly pushes toward more complexity. Glad the numbers spoke for themselves.

Collapse
 
varsha_ojha_5b45cb023937b profile image
Varsha Ojha

This feels very familiar. I’ve had projects where Next.js started feeling like too much framework for what was basically a content site.

Collapse
 
waseemrajashaik profile image
Waseem raja Shaik

That's exactly the moment. Once you realize you're configuring more than you're creating, it's time to simplify.

Collapse
 
varsha_ojha_5b45cb023937b profile image
Varsha Ojha

Yeah exactly, that’s the point where it starts feeling off.
Like you spend more time setting things up than actually building anything meaningful.

Took me a while to realize that β€œmore powerful” doesn’t always mean β€œbetter” for every project

Thread Thread
 
waseemrajashaik profile image
Waseem raja Shaik

Well said. "More powerful doesn't always mean better" is the whole lesson. The best tool is the one that gets out of your way and lets you focus on the content.

Thread Thread
 
varsha_ojha_5b45cb023937b profile image
Varsha Ojha

I agree!

Collapse
 
adarsh_kant_ebb2fde1d0c6b profile image
Adarsh Kant

The Astro migration story resonates β€” framework bloat is real, especially when you don't need the full SPA complexity. The content-first approach with islands architecture makes so much more sense for most websites.

One thing I've noticed working on AnveVoice (we build voice AI that takes real DOM actions on websites): framework choice matters enormously for accessibility and DOM predictability. Static-first approaches like Astro produce cleaner, more semantic HTML which is actually easier for AI agents to parse and interact with. When your voice AI needs to click buttons and fill forms on a website, having predictable DOM structure is gold. Did you notice any accessibility improvements after the migration?

Collapse
 
waseemrajashaik profile image
Waseem raja Shaik

Great point about DOM predictability. The migration did produce cleaner, more semantic HTML since Astro outputs static markup without React's hydration wrappers and synthetic event layers. Hadn't thought about it from an AI agent perspective, but it makes sense that a predictable DOM is easier for voice AI to parse and act on.

Collapse
 
wong2kim profile image
wong2 kim

Great comparison! The 230KB β†’ 0KB JS reduction really shows how Astro shines for content-first sites.
Astro's islands architecture is a game changer β€” keeping React only where it's needed is the right trade-off.

Collapse
 
waseemrajashaik profile image
Waseem raja Shaik

Exactly. Ship JS only where there's actual interactivity. For a blog, that's almost nowhere.

Collapse
 
ai_made_tools profile image
Joske Vermeulen

Great writeup. I went through the exact same migration path, Next.js to Astro, and the numbers are almost identical. The 230KB β†’ 0KB JS on blog posts is the stat that convinced me too.

One thing I'd add: the SEO benefits are real and measurable. My Astro blog started getting Google impressions within days of launching, and I'm pretty sure the Lighthouse 99-100 scores and fast TTFB are contributing to that. Next.js static export can get close, but you're fighting the framework to get there.

The content collections DX is what keeps me on Astro though. Drop an MDX file in a folder, it just works. No next-mdx-remote chain of dependencies.

Curious, are you using Astro's built-in sitemap integration? That + the RSS feed were two things I set up in about 5 minutes that would've been custom code in Next.js.

Collapse
 
waseemrajashaik profile image
Waseem raja Shaik

Yes, using both the sitemap integration and @astrojs/rss. You're right, it was maybe 5 minutes total for both. The SEO improvement is something I'm starting to see too. Google Search Console picked up my pages quickly after deploying. And agreed on content collections DX, just dropping an MDX file and having it work with schema validation built in is hard to go back from once you've used it.

Collapse
 
jtavares profile image
Jonathan Tavares

Astro is such a power house. good move.