Skip to content

Migrating from Next.js to Astro with AI

Mobile Lighthouse scores from the 80s to 100

August 26, 2026
AI-assisted website changes verified by snapshots and optimized to a performance score of 100.

I migrated this site from Next.js to Astro with Claude Code (Fable/High) in less than a day and mobile Lighthouse scores improved from 80-95 to 100. Along the way I also fixed several issues, updated dependencies, and improved the code.

This is inspired by Bun’s Zig to Rust migration. In this case it’s frontend web dev, a tiny codebase, the most complicated part of which is a self-hosted, client-side code sandbox setup. I also didn’t start with many tests.

Using AI I was able to set up visual and performance testing, experiment with several frameworks and optimizations, and do the migration quickly and with no regressions.

Process

  • Add visual regression testing.
    • Prompt: “I want to add visual regress / snapshot testing to this repo. I don’t want to have to use an external service for it. Come up with a plan for me”
    • Framework agnostic with Playwright.
    • 125 snapshot tests, every page, light/dark, mobile/laptop.
    • I prompted the agent to improve the speed of the visual regression tests and it did so by using more precise wait methods, which cut the time by around a minute.
  • Add performance testing.
    • Prompt: “Come up with a plan for measuring some metrics about the site. I want to be able to compare these metrics between builds”
    • Collect metrics like hydration time as well as Lighthouse metrics.
    • Measure navigation time between pages.
  • Add functional testing.
    • Playwright checks of links, feeds and metadata, plus some Vitest browser testing of playground components.
  • Migrate Next 13 → Next 16.
    • Mixed results: the sandbox post got lighter (274 KB to 173 KB of JS) but the home page a little heavier, and the Lighthouse scores went from 95 to 93 on the home page, and 80 to 88 on the sandbox post.
  • Migrate Next Pages Router → Next App Router.
    • Minimal improvement in some metrics, small increase in bundle size.
  • Self-host the fonts instead of using next/font.
    • Total transfer on the home page went from 351 KB to 248 KB and LCP from 2.7 s to 2.0 s; the App Router row in the tables is measured after this.
  • Migrate Next App Router → TanStack Start with React Server Components.
    • Hydration time was about the same, but first paint got slower (FCP roughly doubled).
  • Migrate Next App Router → Astro.
    • Prompt: “Let’s try switching to Astro - https://docs.astro.build/en/install-and-setup/
    • The site is just a blog; it didn’t really need Next.js. All the content is static and can be rendered at build time.
    • This got Lighthouse scores to 100 for home and 99 for sandbox.
    • A later redesign swapped to lighter font files and dropped icon CSS. That change brought all scores to 100.

Results

Mobile, medians over repeated runs. Mobile + throttling increases these numbers a lot and makes it difficult to hit 100 on a page with any complexity. Many sites that claim they hit 100 on Lighthouse only do so on desktop.

Home page

StageJSTotalHydratedFCPLCPScore
Next 13, Pages Router (start)127312226755287295
Next 16, Pages Router152337244906316393
Next 16, App Router163248253755196499
TanStack Start, RSC1181992941806225697
Astro1.490369021503100

Post page with sandboxes

StageJSTotalHydratedFCPLCPScore
Next 13, Pages Router (start)2745163521207405780
Next 16, Pages Router1734343981656375988
Next 16, App Router1674074041055286095
TanStack Start, RSC1213474561953285394
Astro4.517423210521652100
  • JS: kilobytes of gzipped JavaScript the page itself downloads by the time it’s interactive. Sandbox iframes not included.
  • Total: kilobytes of everything the page downloads, gzipped (HTML, CSS, fonts, images, JS).
  • Hydrated: milliseconds from navigation start to the site’s performance.mark('app:hydrated'), when scripts have run and the page is interactive. Measured in Chrome with a 4x CPU slowdown and no network throttling, median of 5 runs.
  • FCP, LCP, Score: First Contentful Paint and Largest Contentful Paint in milliseconds, and the performance score, from Lighthouse’s simulated mobile (slow 4G, 4x CPU slowdown), median of 3 runs.

Navigating from the home page to a post, mobile, 4x CPU slowdown. Next and TanStack navigate client side; Astro loads the new page:

StageTime
Next 13, Pages Router (start)161
Next 16, Pages Router142
Next 16, App Router158
TanStack Start, RSC66
Astro131
  • Time: milliseconds from clicking a link on the home page until the post is rendered: the site’s app:navigated mark for the client-side routers, the new document’s load for Astro.

It’s interesting that TanStack outperforms in client-side navigation, which is in line with its focus on client-side apps.

What went well

  • Building a framework-agnostic snapshot testing and performance testing setup (that doesn’t rely on implementation details) first.
  • Getting the agent to make the testing setup faster.
    • During development the agent ran the tests roughly 200 times. If not for the optimization they would have taken about 3 hours longer to run. So the one prompt of “the testing seems slow, can you make it faster?” paid off a lot.
    • The agent doesn’t have much of a sense of time, and will just put up with things being slow and not mention it.
    • It doesn’t hesitate to run a fresh build or test even when they’re slow, which is often more correct (a human might just keep coding without building or running tests), but exacerbates the issue even more.
  • Experimenting with different ideas.
    • An agent can easily catch regressions using the testing setup so it’s easy to have it try new big changes without much input or risk.
    • I initially thought TanStack was going to be a good move, but it seemed like it performed slightly worse for this use case.
    • A simple blog like this is very suitable for RSC, which is probably why Next.js won since its RSC implementation is more mature.
    • It’s possible that TanStack could beat Next.js with more optimization effort, maybe by switching off RSCs, but I think it would have only been marginal, compared to Astro clearly winning in terms of perf.
    • Without AI, comparing frameworks is usually confined to smaller synthetic comparisons. With this setup I was able to compare four (Next Pages Router, Next App Router, TanStack, Astro) complete, pixel-accurate versions of the whole site within a few hours. Done by hand these comparisons would likely be less thorough, and a full migration would potentially lead to succumbing to sunk-cost thinking.

What could have gone better

  • A couple visual regressions crept in. The agent was often good at reading visual diffs, identifying which part of the image changed and reasoning about it, but it missed some spacing bugs that it thought were noise.
    • I also missed these bugs because my git client doesn’t expand images to 100% zoom.
    • After this I had the agent build a local browser based tool to review the diffs at 100% zoom.
  • Given what I mentioned regarding the snapshot test duration impacting iteration time, it might have been worthwhile to try to get the time down even more.
  • It’s also worth improving build times for the same reason.
    • After the migration to Astro, I asked the agent to investigate build time, and it managed to reduce it from 10s to 2s.
    • The process was kind of like how people use AI to crack math problems by saying “you can do it, keep trying”.
    • Prompt: “3s is not very fast considering how few pages there are, try to make it faster”
    • This is similar to the migration in some ways. You need to have your outputs locked down and consider what changes you’re willing to take on for the optimizations. It got to a point where significant improvement would require complex incremental caching or forking Astro so I decided to stop there.

Conclusions

Developers know migrations or refactors should keep behaviour and appearance identical, and that experimenting should be done thoroughly and without sunk-cost bias. In practice we’re human, and often don’t have the time or stamina for it. With AI we can do things the way we know they should be done, more correctly, and with better results.

One downside I can see of AI in this case is that it let me keep the exact content and design of this site, down to the pixel, when without it I might have thrown things away and made something more creative. I did adjust the design after the migration, however I might have made more drastic changes if I hadn’t been building on top of existing content and code.


Dylan Vann
Dylan Vann
Software developer.

Subscribed!
You should receive an email with a confirmation link!
Or follow me on X for more stuff like this.