Astro 7.2 Can Skip Rebuilding Pages That Didn't Change. On a 506-Post Blog, Half the Win Depends on a Paragraph Nobody Is Quoting.
Astro 7.2 Can Skip Rebuilding Pages That Didn't Change. On a 506-Post Blog, Half the Win Depends on a Paragraph Nobody Is Quoting.
Astro 7.2 landed on August 6 with experimental incremental static builds. Turn on a flag, return a cacheKey from getStaticPaths(), and Astro stops re-rendering prerendered pages whose code and data have not moved since the last build. This blog has 506 posts in a content collection, and on a typical day exactly one of them is new. That is the shape of site the feature was built for.
I read the release notes twice, and the second time I noticed the part that actually decides whether this helps you. It is one paragraph, it is not in any of the coverage I have seen, and it is the reason your mileage will vary enormously.
What actually shipped
The flag goes in astro.config.mjs:
import { defineConfig } from 'astro/config';
export default defineConfig({
experimental: {
incrementalBuild: true,
},
});
Then a route opts in by returning a cacheKey alongside params and props:
export async function getStaticPaths() {
const posts = await getCollection('blog');
return posts.map((post) => ({
params: { slug: post.id },
props: { post },
cacheKey: post.digest,
}));
}
For a content collection, entry.digest is the obvious choice, since it changes whenever the entry's content changes. Paths that do not return a cacheKey are always rendered, which means an existing project builds exactly the way it does today until you go and opt a route in. That is a good default and it makes the upgrade boring, which is what you want from a build-system change.
The cache lives in cacheDir, which defaults to node_modules/.astro/, sitting next to the content layer and image caches. If your CI already persists that directory between runs, you get reuse for free. If it does not, you get nothing, and no error message will tell you that.
The paragraph that decides whether this helps you
Here is the part I keep coming back to. The cacheKey covers your data. It does not cover your code. Astro handles the code side separately: during the build it hashes each route's full module graph, which includes the template, its layouts, its components, its imported assets, and the package code all of that pulls in. If any of that changes, the hash changes, and every path on that route is re-rendered regardless of what the cacheKey says.
A page is reused only when both the module hash and the cacheKey match the previous build.
That is correct behavior. It is also the whole ballgame. Think about what a normal week looks like on a content site. You publish a post, which changes one cacheKey and leaves 505 pages cached. Great. Then on Thursday you tweak the post layout to fix spacing on the author box, and every one of the 506 pages re-renders, because they all share that layout in their module graph. Then you bump a dependency and it happens again.
So the honest way to describe the feature is not "your builds get faster." It is "your builds get faster on days when you only touched content." How many of your days are those? On this blog the content pipeline commits posts daily and I touch layout or components maybe two or three times a month, so the ratio is good. If you are actively developing the site's design, the ratio is terrible and you will spend more time reasoning about cache behavior than you save.
Where this sits relative to Astro 7
Astro 7 already went after build time from the other end. The bundling phase got Rolldown and a Rust-based Markdown pipeline, and the numbers were real: astro.build's own build went from 62.70s to 24.24s, and Cloudflare's 8,431-page developer docs went from 386.89s to 261.94s.
But bundling is roughly fixed work. Generation is the phase that scales with how many pages you have, and until 7.2 Astro re-rendered every static page on every build even when nothing about that page had changed. That is why the Cloudflare docs number is the more interesting of the two: 386s to 261s is a 32% cut on a site with 8,431 pages, and the remaining 261s is dominated by exactly the phase that incremental builds now targets.
Which is a way of saying the feature is aimed correctly. Whether it lands is a separate question, and it is flagged experimental for a reason. The design is still an open RFC.
The failure mode I would actually worry about
Not slowness. Staleness.
A cache that skips work is only safe if the key is genuinely complete. entry.digest covers the entry's content, and the module hash covers code. What sits in neither bucket is anything a page renders from outside those two: a value read from import.meta.env, a fetch to an external API inside the component, a date computed at build time, a count of related posts derived from a different collection.
If your post template renders "12 other posts in this tag," that number comes from a collection query, not from post.digest, and nothing in the cache key knows it changed. You publish a new post in that tag, 505 pages stay cached, and 505 pages now show a stale count. Nothing errors. You just quietly ship wrong numbers until the next time a layout change forces a full re-render and silently fixes it, which is the worst kind of bug because it heals itself before you can catch it.
That is not a knock on the design, it is the normal cost of caching, and the docs are upfront that the cacheKey is your responsibility. But it means opting in a route is a claim you are making about that route, and you should be able to say out loud what the claim is.
What I'd actually do
I am not turning it on here yet, and the reason is not caution about experimental flags.
It is that solooperatorstack.com builds on a cron at 05:00 and deploys to a Hetzner box while I am asleep. The build takes a few minutes. Cutting it to thirty seconds would improve nothing about my day, and in exchange I would take on a class of stale-content bug that is invisible in the output and hard to notice on a site nobody is watching at 5am. Bad trade.
The trade is good if your deploy is in the loop of publishing. If you hit publish and then stand there watching a four-minute build before you can share the link, or if you are paying for CI minutes per build on a docs site with thousands of pages, that is real money and real friction, and this is the feature that fixes it. In that case: turn it on, opt in exactly one route, verify the second build is faster and the output is byte-identical to a clean build, and only then opt in more.
The general rule I would hold to is that a build-cache feature earns its complexity when build time is in your critical path, and does not when it is happening on a schedule while you are doing something else. Astro 7.2 gives you the option. Most content sites should look at their deploy cadence before taking it.
Author
Lukas
@lukcombinator