From React to Astro with Claude
We migrated our entire site from React to Astro in a single sitting. The hard part wasn't the migration — it was admitting the old setup was wrong.
March 2, 2026
Josh was sharing his screen, walking me through a performance issue on one of our sites. The homepage was loading a massive uncompressed JavaScript bundle — React, Next.js, the whole runtime — just to render what was essentially a static page with some text and a few links.
“There was no way to get the performance without moving the app to a subdomain,” he said. He’d already done it for one tool. But I was looking at our marketing site and thinking: why are we shipping a React app for a page that doesn’t need React?
That’s the moment we decided to migrate to Astro.
The setup that made no sense
Our site was a standard React SPA. Every page load shipped the full JavaScript runtime to the browser, hydrated the DOM, and rendered what amounted to static content. The interactive parts — a floating dock component that uses Framer Motion — represented maybe 5% of the actual page. The other 95% was text, images, and links that could have been plain HTML.
This is a pattern I think a lot of small teams fall into. You start building with React because that’s what you know, or because you assume you’ll need interactivity everywhere eventually. By the time you realize most of your pages are static, you’ve already built the whole thing on a framework that ships JavaScript whether you need it or not.
The hardest part of a migration isn’t the technical work. It’s admitting the original choice was wrong.
We weren’t running into scaling issues or hitting some exotic edge case. We were just serving way more JavaScript than any visitor needed. It’s the kind of thing you can ignore for months because the site still works — it’s just slower than it should be, and you’re paying a performance tax on every page load for no reason.
Twenty minutes with Claude
Here’s what actually happened. Josh and I got on a call. He said “should I walk you through installing Astro?” I pulled up the terminal. We ran create astro, picked the blog template, and then prompted Claude to convert the existing React components into Astro files.
Claude removed all the "use client" directives, converted .tsx files to .astro files, and preserved the styling. The one piece that stayed as React was the Dock — the animated floating component that actually needs client-side JavaScript. Astro handles this through what they call “islands architecture”: the page is static HTML by default, and you opt individual components into client-side rendering when they genuinely need it.
The whole conversion — from “let’s try this” to “it builds and runs locally” — took about twenty minutes. Not because we’re fast. Because there just wasn’t that much to change. The site was simple. The content was static. Astro is built for exactly this case.
What Astro actually gives you
If you haven’t looked at Astro, the pitch is straightforward: it’s a web framework that ships zero JavaScript by default. You write components in .astro files that compile to plain HTML. When you need interactivity — a React component, a Svelte widget, whatever — you mark it with a client directive and Astro loads just that piece.
For a marketing site with a blog, this is basically perfect. Pages load instantly because there’s no JavaScript to parse. The build output is static files that deploy anywhere. And when you do need something dynamic, you’re not fighting the framework — you just tell it which components need a runtime.
The thing that surprised me was how little we lost. Our Framer Motion dock still works exactly as before. The styling carried over. The deployment pipeline didn’t change — Cloudflare Workers handles static files just as happily as it handles a full React app. We just stopped shipping megabytes of JavaScript that nobody asked for.
The Claude part is the interesting part
I want to be honest about what Claude did and didn’t do here. It didn’t make some brilliant architectural decision. It did mechanical work — the kind of work that would have taken a developer a day or two of careful file-by-file conversion. Remove this directive, change this extension, restructure this import, preserve this styling.
That’s not nothing. A day or two of migration work is exactly the kind of task that sits on a backlog forever because it’s important but not urgent. You know the site should be faster. You know the architecture is wrong. But there’s always something more pressing, and the migration keeps getting pushed.
What Claude changed wasn’t the difficulty of the migration. It changed the cost. When the cost drops from “two days of tedious work” to “twenty minutes on a call with your co-founder,” you just do it. There’s no backlog debate. There’s no sprint planning. You see the problem, you fix it, you move on.
When the cost of fixing something drops from days to minutes, you stop planning it and start doing it.
What I’d tell a small team considering this
If your marketing site is a React or Next.js SPA and most of your pages are static content, you’re probably shipping JavaScript you don’t need. That’s not a controversial opinion — it’s just what happens when you build everything with a single-page app framework.
Astro is worth looking at. The migration is genuinely not painful, especially if your site is simple. And if you have access to Claude or a similar tool, the mechanical conversion work — the boring, error-prone part — takes minutes instead of hours.
The one thing I’d flag: if you have interactive components, take the time to understand Astro’s island architecture before you start. Knowing which components need client:load versus client:only versus no client directive at all will save you from shipping JavaScript you just got rid of.
We should have done this months ago. The only reason we didn’t was inertia — the site worked, so we never prioritized making it work better. Sometimes the best thing an AI tool does isn’t solve a hard problem. It’s eliminate the excuse for not solving an easy one.
Share this article: