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
The best way to host your Next.js site is on the platform built by the creators of Next.js!
Vercel is built by the team behind Next.js, so every Next.js feature works there on the day it ships, including the proxy, on-demand revalidation and Cache Components. This guide covers deploying an Agility-powered Next.js site, wiring up publish-triggered updates, and the two settings that most often break preview after a successful deploy.
The fastest path. In the Agility Manager, go to Settings → Deployment and click Setup Deployment for Vercel.
Note: You'll need a GitHub account and a Vercel account.
The wizard then:
AGILITY_* environment variables on the Vercel project, for both Production and Preview.That last step is what makes preview work without you configuring anything: Agility now knows which URL to open when an editor clicks Preview.
If your project isn't based on a starter:
Push your repository to GitHub, GitLab or Bitbucket.
At vercel.com/new, import it. Vercel detects Next.js and needs no build configuration.
Add your environment variables:
| 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 |
AGILITY_SITEMAP | Sitemap channel name, usually website |
Set them for Production, Preview and Development. A missing key on Preview is the classic cause of "it works in production but every preview deploy 500s."
Deploy, then install the Agility CMS integration from the Vercel Marketplace to link the project back to your instance.
Don't rebuild the site on every publish. The Agility Next.js Starter tags every Agility fetch with a cache tag, and a publish webhook invalidates exactly the tags that changed, so the update is live on the next request and nothing else re-renders.
Each read in the starter's lib/cms/ helpers sets the tag on the fetch:
// lib/cms/getContentItem.ts (starter)
agilitySDK.config.fetchConfig = {
next: {
tags: [`agility-content-${params.contentID}-${params.languageCode || params.locale}`],
revalidate: 60,
},
}
and the starter's route handler clears those tags when Agility sends a publish event:
// app/api/revalidate/route.ts (starter, abridged)
import { revalidateTag } from "next/cache"
import { NextRequest, NextResponse } from "next/server"
export async function POST(req: NextRequest) {
const data = await req.json()
if (data.state === "Published") {
if (data.referenceName) {
// content item or list change
revalidateTag(`agility-content-${data.referenceName}-${data.languageCode}`, "max")
revalidateTag(`agility-content-${data.contentID}-${data.languageCode}`, "max")
} else if (data.pageID > 0) {
// page change: the page and the sitemaps
revalidateTag(`agility-page-${data.pageID}-${data.languageCode}`, "max")
revalidateTag(`agility-sitemap-flat-${data.languageCode}`, "max")
revalidateTag(`agility-sitemap-nested-${data.languageCode}`, "max")
}
}
return NextResponse.json({ message: "OK" }, { status: 200 })
}
In Next.js 16,
revalidateTagtakes a second argument, the cache profile."max"(recommended) serves the stale page while the fresh one renders in the background. The one-argument form is deprecated and TypeScript rejects it, so a typed project fails to build without it.
The endpoint is public, so check that each request really comes from Agility before you act on it. See Verifying Signed Webhooks.
If you've enabled Cache Components, the same webhook works: you tag reads with cacheTag() inside "use cache" functions instead of on the fetch.
In the Manager, go to Settings → Web Hooks and add one pointing at https://your-site.com/api/revalidate. Check Receive Content Publish Events, and send a Test Payload to confirm you get a success response before relying on it.
If you want to trigger a full rebuild instead (appropriate only if you don't use tag revalidation), create a Deploy Hook in Vercel under Settings → Git → Deploy Hooks and point the Agility webhook at that URL.
Two things bite here, and both only appear once the site is actually on Vercel.
Vercel serves prerendered pages straight from its edge cache without invoking your proxy. On exactly the pages that matter most, ?agilitypreviewkey= never reaches /api/preview, draft mode is never enabled, and Web Studio quietly shows published content.
The fix is 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 the proxy logic as well, since it handles uncached requests. Full detail in Preview URL Lifecycle.
Vercel protects preview deployments by default. Agility's preview requests and your webhook calls arrive unauthenticated and get an SSO page instead of your site, so preview shows a Vercel login screen, and publishes appear not to revalidate.
Under Settings → Deployment Protection, either add a Protection Bypass for Automation secret and configure it in your Agility integration, or scope protection so the domain Agility calls is reachable. Don't simply switch protection off.
"engines": { "node": "24.x" } in package.json (which overrides the project setting). Node 18 and 20 are end-of-life; Next.js 16 needs at least 20.9.prebuild: if your project syncs redirects or other data before building (npm runs prebuild automatically before build), confirm that step has the env vars it needs on Vercel, not just locally..next/cache between builds, which is what keeps incremental builds fast. Use Redeploy without cache when debugging a build that succeeds locally but not on Vercel.curl -I https://your-site.com/about-us # 200, with cache headers
curl -I https://your-site.com/no-such-page # a real 404, not a 200
curl -X POST https://your-site.com/api/revalidate \
-H "Content-Type: application/json" -d '{"state":"Published", ...}'
Then publish a change in Agility and confirm it appears on the live site within seconds. That end-to-end test is what actually proves the wiring.
AGILITY_* variable on all three environments.revalidateTag(tag, "max"), instead of rebuilding on every publish.beforeFiles preview rewrite, or preview breaks on exactly the cached pages editors care about.