Cache issues with Sanity and Next.js resolved using server-side rendering
This is a classic caching issue when using Next.js with Sanity! The 5-10 minute delay you're experiencing is likely due to multiple caching layers working together. Let me break down what's happening and how to fix it.
Why This Happens
You're dealing with two separate caches:
- Sanity's CDN cache (
apicdn.sanity.io) - The API CDN has its own caching policy that caches responses for performance - Next.js/Vercel cache - If you're using ISR (Incremental Static Regeneration) or static generation, your pages are cached by Next.js
When you publish content, it updates in Sanity's Content Lake immediately (which is why you see it locally if you're querying without the CDN), but the cached versions take time to update.
The Solution: On-Demand Revalidation with Webhooks
The best approach is to set up on-demand revalidation using Sanity webhooks. This tells Vercel to immediately rebuild affected pages when you publish content. Here's how to set it up:
Step 1: Create a Revalidation API Route
In your Next.js app, create an API route. For the App Router, create app/api/revalidate/route.ts:
import { revalidateTag } from 'next/cache'
import { type NextRequest, NextResponse } from 'next/server'
import { parseBody } from 'next-sanity/webhook'
export async function POST(req: NextRequest) {
try {
const { body, isValidSignature } = await parseBody(
req,
process.env.SANITY_REVALIDATE_SECRET
)
if (!isValidSignature) {
return new Response('Invalid signature', { status: 401 })
}
if (!body?._type) {
return new Response('Bad Request', { status: 400 })
}
// Revalidate specific tags based on document type
revalidateTag(body._type)
return NextResponse.json({
status: 200,
revalidated: true,
now: Date.now()
})
} catch (err) {
console.error(err)
return new Response('Internal Server Error', { status: 500 })
}
}For the Pages Router, create pages/api/revalidate.ts:
import type { NextApiRequest, NextApiResponse } from 'next'
import { parseBody } from 'next-sanity/webhook'
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
try {
const { body, isValidSignature } = await parseBody(
req,
process.env.SANITY_REVALIDATE_SECRET
)
if (!isValidSignature) {
return res.status(401).json({ message: 'Invalid signature' })
}
// Revalidate the path for this document
await res.revalidate('/blog') // Adjust to your blog path
return res.json({ revalidated: true })
} catch (err) {
return res.status(500).json({ message: 'Error revalidating' })
}
}Step 2: Set Up a Sanity Webhook
- Go to your Sanity project dashboard at sanity.io/manage
- Navigate to API β Webhooks
- Create a new webhook with:
- URL:
https://your-site.vercel.app/api/revalidate - Dataset: Your dataset (usually
production) - Trigger on: Create, Update, Delete
- Filter: Optionally filter to specific document types like
_type == "post" - Secret: Generate a secret and add it to your Vercel environment variables as
SANITY_REVALIDATE_SECRET
- URL:
Step 3: Disable Sanity CDN for Production Builds
In your Sanity client configuration, set useCdn: false to ensure you're getting fresh data during builds. The Sanity CDN and Next.js ISR can work at cross-purposes:
import { createClient } from 'next-sanity'
export const client = createClient({
projectId: 'your-project-id',
dataset: 'production',
apiVersion: '2024-01-01',
useCdn: false, // Disable CDN for fresh data
})Modern Alternative: Sanity Functions
If you're on a Growth plan or higher, consider using Sanity Functions instead of webhooks. Functions run serverless code directly in Sanity when content changes, eliminating the need for external webhooks entirely. This is the modern, recommended approach for handling content events, as it provides better security, automatic scaling, and native integration with Sanity.
Quick Fix: Time-Based Revalidation
If you need a simpler solution right now, you can use time-based ISR revalidation in your data fetching:
// App Router with fetch
const posts = await fetch('https://your-project.api.sanity.io/...', {
next: { revalidate: 60 } // Revalidate every 60 seconds
})
// Or with sanityFetch
const posts = await sanityFetch({
query: POSTS_QUERY,
revalidate: 60, // Revalidate every 60 seconds
})This won't give you instant updates, but it'll reduce the delay from 5-10 minutes to 1 minute.
The on-demand revalidation approach is definitely the way to go for production sites where content authors expect immediate updates after publishing!
Sanity β Build the way you think, not the way your CMS thinks
Sanity is the developer-first content operating system that gives you complete control. Schema-as-code, GROQ queries, and real-time APIs mean no more workarounds or waiting for deployments. Free to start, scale as you grow.