# The prompt that converted this site from Next.js to Astro

A prompt cannot convert a Next.js site to Astro on its own, but a written plan with phases and done checks can drive an agent through the conversion. This site moved from Next.js 13 with 15 runtime dependencies and 17 React pages to Astro with 8 dependencies, zero JavaScript bundles, and a 940 KB build. This log is the plan, what the agent did unassisted, and where a human had to decide.

Published: 2026-09-23
Canonical: https://umar.codes/next-to-astro-conversion

## Revisions

- 2026-09-16 — Created as an outline. Sourced from Search Console: the query "astro to next.js conversion prompt" earned 12 impressions at position 25 with no dedicated post.
- 2026-09-16 — Body written from the rebuild plan, DISCOVERY.md, and the decision log of 2026-07-27.
- 2026-09-23 — Published, two days before its slot: the publish-day demand check swapped it with the ag-grid-url-sync grammar reference (11 impressions against 4). Opening trimmed from 67 to 57 words; linked the crawler-log post.

---

A single prompt does not convert a Next.js site to Astro. A 330-line plan with
ten phases and a done check per phase does, when an agent works through it.
This site went from Next.js 13 with 15 runtime dependencies and 17 React pages
to Astro with 8 dependencies and zero JavaScript bundles in one day, 2026-07-27.

## Why a plan and not a prompt

The searches that bring people to this page ask for a conversion prompt: one
message that turns a Next.js repo into an Astro one. I did not have one. What I
had was a markdown file, 330 lines long, with ten phases numbered 0 to 9. Each
phase states an outcome, a task list, and a done check. The first rule in the
file is the one that matters: do not move to the next phase until the done
check passes.

A prompt says what you want. A plan says how you will know you got it. The
agent that ran this plan could not skip discovery because Phase 1 needed the
route list from Phase 0. It could not skip redirects because Phase 7's done
check reads every URL from the discovery file and expects a 200 or a 301. The
plan has four more rules: commit at the end of every phase, never deploy,
record judgement calls in a decisions file, and write blockers to a blockers
file instead of stopping. There is exactly one hard gate, and I come to it
below.

## What the Next.js site was

Before touching anything, the agent wrote a discovery file. That file says the
old site was Next.js 13 on the pages router, React 18, Chakra UI 2 with
Emotion and framer-motion, and yarn 1. It had 15 runtime dependencies, 8 dev
dependencies, and 69 TypeScript files across 22 component folders.

It served 17 routes. Seven of them were blog posts from November and December
2021, and every one of those posts was a `.tsx` file. The prose lived inside
React components, next to a Calendly popup and a Formspark contact form. Four
more routes were service pages for an agency I no longer run.

That is the real conversion cost, and no migration guide mentions it. The
guides say rename `pages/*.jsx` to `src/pages/*.astro` and swap `<Link>` for
`<a>`. That works when the content has a format of its own. Mine did not. The
content had to be pulled out of components before anything could be converted,
and once it was out, most of it turned out not to be worth converting.

## Phase 0: write down what exists before touching it

The plan's first phase produces no code. It produces a file that lists every
route, every project entry with its URL, every social link, and every piece of
prose on the site. The plan's own justification: the project list is reused in
Phase 7, and losing it means re-scraping the live site.

For a person, discovery is optional because the person remembers. For an agent,
it is the only memory that survives the archive step. When the agent moved the
old source into a folder called `_archive`, the discovery file was the one
document that still described what the site used to do. Every later done check
reads from it.

## The one place the agent was told to ask

Phase 1 chooses the framework. The default is Astro. The gate reads: if the
repo is Next.js or Remix and the human has strong reason to stay there, this is
the one place to ask. Post the tradeoff in one short message and wait.
Everywhere else, proceed.

Why that gate and no others? Because the framework choice is the only decision
in the plan that a done check cannot settle. Whether a build passes, whether a
link resolves, whether a draft leaks into the sitemap: those are checkable.
Whether to leave React is taste and history. I had already built this site in
[Next.js, Remix, and Astro](/three-frameworks) and knew the answer, so the gate
opened and closed in one message. But it was there, and its position in the
plan is the design.

The agent made 13 recorded decisions on that first day without asking. It
picked pnpm over yarn because the done checks were written as `pnpm build`. It
used Astro 7 when the plan said Astro 5, because the APIs it needed were the
same shape. It replaced the sitemap integration with a custom endpoint because
the integration could not exclude drafts. Each one is a dated line in the
decisions file, with the plan's wording and the reason for diverging.

## What came across and what did not

The archive commit moved 124 files in one step. Nothing was deleted; the plan
says to keep the old source until Phase 7 confirms nothing references it.

What migrated: the identity data, the project list with its stack labels and
URLs, and the social links. That is it.

What did not: all seven blog posts and all four service pages. The posts were
generic agency SEO content with no first-person substance, and the decisions
file says so in those words. They were retired, not converted. The old
`/blog/:slug` routes now return a 301 to `/writing`; `/portfolio` goes to
`/projects`; `/contact` goes to `/about`; only `/services/*` falls back to the
homepage.

So the honest name for what happened is not conversion. It is retirement plus
rebuild, with redirects for everything that used to have a URL. I think that is
true of most Next.js to Astro migrations on personal sites. The framework
change is the excuse; the content audit is the work.

## The build after

The Astro site has 8 runtime dependencies and 17 dev dependencies. The source
tree is 57 files. A production build takes about 3 seconds and produces 940 KB
on disk, and 608 KB of that is self-hosted fonts. There are no JavaScript
bundles. The only scripts on any page are two inline snippets for theme
persistence and two JSON-LD blocks.

The framework comparison lives in the [three frameworks essay](/three-frameworks).
This log is about the process, and the process number I care about is one day
from discovery to a site that passed its own verification phase: build clean,
Lighthouse at or above 95 on all four categories, every old URL resolving or
redirecting, no draft in any feed.

## What I would put in the plan if I did it again

Three things the plan did not say, the agent did not guess, and I found later.

**Robots rules for hashed asset paths.** The scaffold's `robots.txt` disallowed
`/_astro/` for every user agent. That folder holds the only stylesheet and both
preloaded fonts, so Googlebot rendered every page unstyled for two weeks. The
fix landed on 2026-08-10, and the [crawler log](/logging-ai-crawlers) was the
only reason I found it.
The plan should say: never disallow a path that serves CSS or fonts.

**Redirects for URLs you forgot you had.** The plan's redirect map came from the
discovery file, and the discovery file came from the running site. But the
running site had already dropped an older `/posts/*` scheme. The crawler log
showed 11 distinct `/posts/*` URLs still being requested, about 30 hits in 13
days, all returning 404. The plan should say: pull the redirect list from
Search Console and server logs, not only from the current routes.

**A pinned TypeScript version.** The first build failed `astro check` until
TypeScript was pinned, and that was the second commit of the day. It is a small
thing. It is also exactly the kind of thing a plan exists to hold, so the next
run does not rediscover it.

If you want the prompt, that is it: a plan with done checks, one gate for the
decision only a human can make, a decisions file for every judgement call, and
a log that tells you what the plan missed.
