What is your developer-dependent CMS actually costing you? Calculate your costs and get a full report
What is your developer-dependent CMS actually costing you? Calculate your costs and get a full report
Tutorials
Netlify runs Next.js through its Next.js Runtime, which is detected and installed automatically — App Router, React Server Components, streaming, route handlers, the proxy and on-demand revalidation all work. This guide covers deploying an Agility-powered site, wiring publish-triggered updates, and the two things that most often break preview after a green deploy.
Coming from the older recipe? This article used to tell you to let Netlify install the Essential Next.js Build Plugin and warned that "Netlify Preview for Next.js is still experimental." Both are out of date. The Essential Next.js plugin was superseded by the Next.js Runtime (v5), which Netlify installs for you — you should not be adding
@netlify/plugin-nextjstonetlify.tomlby hand on a new site. And preview is no longer experimental; it just needs the configuration in the "Making preview work" section below. The old guide also pointed you at Settings → Sitemaps, which is now Settings → Deployment.
In the Agility Manager, go to Settings → Deployment and click Setup Deployment, then choose the Netlify automated deployment.
Note: You'll need a GitHub account and a Netlify account.
The wizard authorizes Netlify against your Git provider, creates the repository, sets your AGILITY_* environment variables, builds, and writes the resulting domain back into your Agility instance.
Push your repository to GitHub, GitLab or Bitbucket.
In Netlify, Add new site → Import an existing project, and pick the repo. Netlify detects Next.js; leave the build command (npm run build) and publish directory alone — the runtime handles them.
Under Site configuration → Environment variables, add:
| Variable | Purpose |
|---|---|
AGILITY_GUID | Your instance identifier |
AGILITY_API_FETCH_KEY | Live (published) content |
AGILITY_API_PREVIEW_KEY | Draft content, for preview |
AGILITY_SECURITY_KEY | Validates preview links and webhooks |
AGILITY_LOCALES | Comma-separated, first is the default — no spaces |
AGILITY_SITEMAP | Sitemap channel name, usually website |
Make sure they're scoped to all deploy contexts, not just production. A variable missing from Deploy Previews is the usual reason a PR preview 500s while production is fine.
Deploy.
Then register the deployed URL in Agility under Settings → Deployment → Setup Deployment → Custom Deployment, so editors' preview links point at the right host.
You do not need a full rebuild to publish a change. With Cache Components, each content read is cached under a tag:
const cachedContentItem = async (params) => {
"use cache"
cacheTag(`agility-content-${params.contentID}-${params.languageCode}`)
cacheLife("days")
return fetchContentItem(params)
}
and a route handler clears the tag when Agility says something changed:
// app/api/revalidate/route.ts
import { revalidateTag } from "next/cache"
export async function POST(req: NextRequest) {
const data = await req.json()
if (data.state === "Published" && data.referenceName) {
// Next 16 requires the second argument — the cache profile to expire
revalidateTag(`agility-content-${data.referenceName}-${data.languageCode}`, "max")
}
return NextResponse.json({ revalidated: true })
}
revalidateTag(tag)with one argument is a Next 15 signature. In Next 16 it takes a cache-life profile as a second argument, and omitting it is a build error.
In the Manager, go to Settings → Web Hooks and point a hook at https://your-site.com/api/revalidate, with Receive Content Publish Events checked. Send a Test Payload and confirm a success response before relying on it.
For a fully static site, trigger a build instead:
A rebuild takes minutes and re-renders the whole site; tag revalidation takes seconds and re-renders only what changed. Prefer the webhook above unless you have a reason not to.
This is the one that costs people a day. Netlify serves prerendered pages straight from its CDN without invoking your proxy (proxy.ts — Next 16's renamed middleware.ts). So on exactly the pages that matter most, ?agilitypreviewkey= never reaches /api/preview, draft mode is never enabled, and Web Studio silently renders the published page. No error appears anywhere, and you cannot reproduce it locally, because under next start the proxy always runs.
Fix it with a beforeFiles rewrite, which is compiled into the routes manifest and evaluated before the cache lookup:
// next.config.mjs
async rewrites() {
return {
beforeFiles: [
{
source: "/:path((?!api).*)",
has: [{ type: "query", key: "agilitypreviewkey" }],
destination: "/api/preview?slug=/:path",
},
],
}
}
Keep your proxy logic as well — it handles uncached requests, and the two converge on the same handler. Full detail in Handling Preview URLs & Request Lifecycle.
Draft mode shows unpublished content. If your cache headers are unconditional, that content gets stored on a CDN and served to the public. Set them where the draft cookie is visible — in the proxy:
const DRAFT_COOKIE = "__prerender_bypass"
if (request.cookies.has(DRAFT_COOKIE)) {
res.headers.set("Cache-Control", "private, no-store")
} else {
res.headers.set("CDN-Cache-Control", "public, s-maxage=60, stale-while-revalidate=86400")
}
CDN-Cache-Control is honoured by Netlify and Vercel alike, so this stays vendor-neutral.
Netlify builds a Deploy Preview for every pull request automatically. Two things to check:
AGILITY_* variables are available in the Deploy Preview context, not just Production.curl -I https://your-site.com/about-us # 200, with CDN cache headers
curl -I https://your-site.com/no-such-page # a real 404, not a 200
Then publish something in Agility and confirm it appears within seconds — the end-to-end test that actually proves the webhook, the tag and the cache are all wired together.
AGILITY_* variable in all deploy contexts.revalidateTag(tag, "max") over a full rebuild.beforeFiles preview rewrite, or preview breaks on exactly the cached pages editors care about — silently.