Building with AI

Statically Rendered, Instantly Fresh

Static pages that update the moment I publish. How a Sanity webhook triggers on-demand revalidation so a fully static Next.js site never serves stale content.

Val OvinnikovVal Ovinnikov3 min read
A crystalline static page catching a single spark from a distant publish button, refreshing its glow instantly, warm amber-teal dusk.

Static sites are fast because the work happened at build time — and stale for the same reason. The classic fix is to rebuild the whole site on every edit, which is slow and wasteful for a typo. This blog is fully statically rendered and still updates the instant I publish, because a Sanity webhook tells Next.js to revalidate exactly the pages that changed. Fast by default, fresh on demand. Part of the Building with AI series.

Static first, for speed

Every post page is generated ahead of time. Next.js pre-renders the known routes at build, so a reader gets finished HTML from the edge with no database call in the request path. The list of routes to build is, like everything else, a read through the typed service layer:

apps/web/src/app/blog/[slug]/page.tsx -->
1import { getAllSlugs, getPostBySlug } from "@repo/service";
2
3export async function generateStaticParams() {
4  const slugs = await getAllSlugs();
5  return slugs.map((s) => ({ slug: s.current }));
6}
7
8export const revalidate = false; // static until something tells us otherwise

Publish fires a webhook

The freshness comes from a signal, not a schedule. Sanity is configured to POST to a route in the app whenever a blog_post is published. That handler doesn't rebuild the site — it invalidates just the affected cache tags, so the next request to those pages regenerates and everything else stays untouched.

apps/web/src/app/api/revalidate/route.ts
1import { revalidateTag } from "next/cache";
2import { parseBody } from "@sanity/webhook";
3
4export async function POST(req: Request) {
5  const { isValidSignature, body } = await parseBody(req, process.env.SANITY_WEBHOOK_SECRET);
6  if (!isValidSignature) return new Response("Invalid signature", { status: 401 });
7
8  revalidateTag("post:" + body.slug);
9  revalidateTag("post-list");
10  return Response.json({ revalidated: true });
11}

The signature check on the way in is not optional — the highlighted revalidateTag is a public endpoint doing real work, so it verifies Sanity's secret before it trusts the payload. An unsigned request gets a 401 and nothing moves.

Tags tie data to pages

The reason a single publish can refresh the right pages is that the fetches are tagged. When the service layer reads a post, it tags that cache entry with the post's slug and a shared post-list tag. Invalidating post:my-slug drops exactly the detail page; invalidating post-list refreshes the home and archive cards. No full rebuild, no guesswork.

Static isn't the opposite of fresh. Cache invalidation just needs a trigger, and publishing is the perfect one.

Why this fits the rest of the build

This is the same shape as every other decision in the series: derive from one source, and let a signal — not a human — keep things in sync. The metadata generates itself, the sitemap is a query, and now the cache heals itself on publish. An agent can add a page or a fetch without learning a bespoke deploy dance, because freshness is a property of the tagging convention, not a manual step.

Serve static, invalidate on publish, and you get edge speed with live-CMS freshness. The reader never waits on the database, and I never wait on a rebuild. Last in the series, an honest accounting of the whole thing — 24 Days, ~1,250 Commits: What the Agents Got Right and Wrong.

2 min

Rendering Portable Text in Next.js 16

Portable Text is just JSON until you render it. How I mapped Sanity's block format to React components — headings, links, and real code blocks — in the App Router.

building with ai
3 min

This Blog Was a Weekend Idea

How an idea over coffee became a production blog in 24 days and ~1,250 commits, built mostly by AI agents — with a map to every post that tells the story.

building with ai
3 min

SEO That Generates Itself

No hand-written meta tags, ever. How titles, Open Graph, sitemap and RSS all fall out of the same schema the posts already use — generated, not maintained.

building with ai