Astro 7 shipped on June 22, 2026. I waited until 7.0.7 to avoid the early-patch churn, then upgraded this blog yesterday. Three files changed. Here’s what happened.
Table of contents
Open Table of contents
Why bother when Astro 6 works
Astro 6 was fine. The blog built, deployed, served. Nothing was on fire.
I upgraded anyway because the parts of Astro 7 that matter are infrastructure, not features:
| Change | What it does |
|---|---|
| Vite 8 + Rolldown | Replaces Rollup + esbuild with one Rust bundler. Astro’s benchmarks put it at 15-61% faster builds. Gerald Chen measured no speedup on a 48-page blog, so YMMV. |
| Rust compiler | Replaces the Go compiler. Faster, smaller memory footprint. Not user-visible unless you’re hitting build OOMs. |
| Sätteri | Rust markdown pipeline. Doesn’t run remarkPlugins/rehypePlugins out of the box (see below). |
| Queued rendering | Pages render in parallel batches. Invisible on small sites. |
Astro 6 will be the previous major in a few months and the plugin ecosystem is already moving. That’s the real reason.
The migration in three diffs
1. package.json — bumped and dropped
- "@astrojs/preact": "^5.1.5",- "@astrojs/rss": "^4.0.18",- "@digi4care/astro-google-tagmanager": "^1.6.0",- "astro": "^6.4.7",- "astro-expressive-code": "^0.41.7",+ "@astrojs/preact": "^6.0.1",+ "@astrojs/rss": "^4.0.19",+ "astro": "^7.0.7",+ "astro-expressive-code": "^0.44.0",Two things happening. First, the straightforward version bumps. Second, I dropped @digi4care/astro-google-tagmanager entirely. The package was used in exactly one place: <SiteVerification id="..." />, which renders as a single <meta name="google-site-verification" content="..."> tag. That’s not a dependency, that’s a line of HTML.
2. astro.config.ts — the unified processor shim
This was the one part I had to think about. Astro 7 defaults to Sätteri for markdown processing. Sätteri is faster, doesn’t run on Node, and doesn’t understand my existing remark/rehype plugins. My config uses four of them: remark-toc, remark-collapse, rehype-slug, rehype-autolink-headings.
Skipping the @astrojs/markdown-remark shim will silently drop your remarkPlugins and rehypePlugins. Sätteri runs first; the JS pipeline never executes. Astro 7 fails fast with a clear error during config validation, but the fix is the same: install @astrojs/markdown-remark and wrap your plugins in unified({...}).
The fix is one extra import and one wrapper:
+import { unified } from "@astrojs/markdown-remark"; import remarkToc from "remark-toc"; import remarkCollapse from "remark-collapse"; import rehypeSlug from "rehype-slug"; import rehypeAutolinkHeadings from "rehype-autolink-headings";
markdown: {- remarkPlugins: [remarkToc, [remarkCollapse, { test: "Table of contents" }]],- rehypePlugins: [rehypeSlug, [rehypeAutolinkHeadings, { behavior: "wrap" }]],+ processor: unified({+ remarkPlugins: [remarkToc, [remarkCollapse, { test: "Table of contents" }]],+ rehypePlugins: [rehypeSlug, [rehypeAutolinkHeadings, { behavior: "wrap" }]],+ }), },@astrojs/markdown-remark ships with Astro 7 for exactly this case. It’s the same unified pipeline Astro 5 and 6 used, wrapped as an explicit processor. Install it once, wrap once, done.
I could have migrated to Sätteri’s native mdastPlugins/hastPlugins and dropped the four npm packages. I didn’t, for two reasons. First, the plugins are still maintained and the unified wrapper adds no measurable build time on a site this size. Second, I’d rather write one config block than learn a new plugin API and rewrite the ToC behavior I already have. If remark-toc dies or Sätteri becomes mandatory, I’ll deal with it then.
3. src/layouts/Layout.astro — the inlined meta tag
-import { SiteVerification } from "@digi4care/astro-google-tagmanager";-- <SiteVerification id="your-google-site-verification-token" />+ <meta name="google-site-verification" content="your-google-site-verification-token" />That’s the entire GTM integration in this repo. One import, one meta tag. Inlining cost nothing.
One bonus fix: the caddy code fence
After upgrading, astro check warned that a code block in my Caddy post used caddy as its language, and Shiki doesn’t ship a Caddy grammar. The block was already rendering as plain text because the fallback fired silently in Astro 6. Astro 7 turned it into a real warning.
No community grammar exists for Shiki, as far as I could find. Caddyfile syntax borrows heavily from nginx, so I switched the fence:
```caddy title="Caddyfile"```nginx title="Caddyfile"Same block, real highlighting. One line.
What I didn’t migrate
- Sätteri-native plugins. The
unified({...})shim keeps the JS pipeline. Faster to migrate, slower than native. I’ll revisit if/whenremark-tocstops working. src/fetch.tsadvanced routing. New in Astro 7, opt-in. I don’t need a custom request pipeline for a static blog served by Caddy.- CDN cache providers. Astro 7 added experimental providers for Netlify, Vercel, Cloudflare. Site is on a VPS. No value.
- JSON logger for AI agents. Detects coding agents, emits structured JSON logs. Cute. I don’t run agents against this site in a way that would benefit.
- Rolldown custom plugin API. I have no custom Vite plugins. Nothing to migrate.
If you’re on Astro 6 today
Run bunx @astrojs/upgrade and read the output. For an AstroPaper-style blog with remark/rehype plugins, the only real decision is whether to install @astrojs/markdown-remark and keep the unified pipeline, or migrate to Sätteri’s plugin API. The first option is ten minutes of work. The second is a few hours and unlocks the full speedup.
bunx @astrojs/upgrade bumps Astro and its official integrations together. It will not bump third-party integrations like @digi4care/astro-google-tagmanager (which is why I’m mentioning it — I’ve been bitten before). After running it, audit package.json manually.
I picked the first. The blog is small, the build was already fast, and I had other things to do.