Use Sanity with Next.js

Validating Sanity webhooks in Next.js

Set up webhook-based cache revalidation in Next.js using parseBody from next-sanity/webhook. Covers path-based and tag-based revalidation for App Router and Pages Router.

When content changes in Sanity, you can use webhooks to revalidate cached pages in your Next.js application on demand. The next-sanity toolkit includes parseBody, a utility that validates webhook signatures and parses payloads, so you can build secure revalidation handlers with minimal code.

This guide covers two revalidation patterns:

  • Path-based revalidation: revalidate specific routes when a document changes
  • Tag-based revalidation: revalidate all pages that depend on a document type

Both patterns work with App Router and Pages Router API routes.

Prerequisites

  • A Next.js application with next-sanity configured.
  • A Sanity project with permission to create webhooks.
  • A shared secret for webhook signature validation.

Set up the shared secret

Create a SANITY_REVALIDATE_SECRET environment variable with a random string. This secret must match in both your Sanity webhook configuration and your Next.js application.

Add the same value to your hosting provider's environment variables (Vercel, Netlify, etc.).

The parseBody utility

parseBody from next-sanity/webhook handles signature validation and payload parsing in one call:

import { parseBody } from 'next-sanity/webhook'

const { isValidSignature, body } = await parseBody<MyPayloadType>(
  request,
  secret,
  waitForContentLakeEvent
)

The function accepts three arguments:

ArgumentTypeDescription
requestRequestThe incoming webhook request
secretstringYour SANITY_REVALIDATE_SECRET value
waitForContentLakeEventboolean (optional)When true, adds a short delay before returning. This gives the Content Lake time to propagate changes, preventing your revalidation from fetching stale data. Defaults to false.

parseBody returns an object with:

  • isValidSignature: true if the webhook request was signed with the correct secret
  • body: the parsed webhook payload, typed as the generic you provide

Path-based revalidation

Use path-based revalidation when you know which routes correspond to which documents. For example, a blog post at /posts/my-post maps directly to a Sanity document with slug.current === 'my-post'.

Create the API route

Configure the webhook projection

The webhook needs to send a path value that matches your Next.js routes. Use a GROQ projection with the select() function to generate paths dynamically:

{
  "path": select(
    _type == "post" => "/posts/" + slug.current,
    "/" + slug.current
  )
}

Extend this pattern for your own document types and route structure:

{
  "path": select(
    _type == "post" => "/posts/" + slug.current,
    _type == "author" => "/authors/" + slug.current,
    _type == "category" => "/categories/" + slug.current,
    "/" + slug.current
  )
}

Tag-based revalidation

Use tag-based revalidation when a single document change affects many pages. For example, updating an author's name should revalidate every post that displays it.

Tag-based revalidation has two parts:

  • Tagging queries: when fetching data, you associate queries with tags (covered in Caching and revalidation in Next.js)
  • Busting tags: when content changes, a webhook calls revalidateTag() to invalidate all queries with that tag

This section covers the webhook side.

Create the API route

How tags connect to queries

When you fetch data using the manual sanityFetch helper with tags:

const posts = await sanityFetch({
  query: POSTS_QUERY,
  tags: ['post', 'author'],
})

Next.js associates the cached response with both the post and author tags. When the webhook fires for a document with _type === 'post', calling revalidateTag('post') invalidates all cached queries tagged with 'post'.

See Caching and revalidation in Next.js for the full sanityFetch helper implementation and tagging patterns.

Configure the webhook projection

For tag-based revalidation, the projection is simple. You only need the document type:

{_type}

Setting up the webhook in Sanity

Configure the webhook in your Sanity project dashboard:

FieldPath-basedTag-based
URLhttps://your-domain.com/api/revalidate-pathhttps://your-domain.com/api/revalidate-tag
EventsCreate, Update, DeleteCreate, Update, Delete
Filter_type in ["post", "author"]_type in ["post", "author"]
Projection{"path": select(...)}{_type}
SecretYour SANITY_REVALIDATE_SECRETYour SANITY_REVALIDATE_SECRET
HTTP methodPOSTPOST

Adjust the filter to include the document types your application uses.

Quick setup with webhook templates

Use these shareable templates to create pre-configured webhooks in your Sanity project:

After importing a template, update the URL to point to your deployed application and set the secret.

Security

Webhook endpoints are publicly accessible URLs. Follow these practices to keep them secure:

  • Always validate signatures. Never skip the parseBody validation step, even in development. An unvalidated endpoint lets anyone trigger revalidation (or worse, if you add write operations later).
  • Use a strong secret. Generate a random string of at least 32 characters. Tools like openssl rand -base64 32 work well.
  • Keep the secret out of version control. Use .env.local for local development and your hosting provider's secrets management for production.
  • Consider rate limiting. In production, add rate limiting to your revalidation endpoints to prevent abuse. Most hosting providers offer this at the infrastructure level.

Pages Router

Webhook revalidation works with Pages Router API routes. The Live Content API is App Router only, but webhook-based revalidation is available on both routers.

The key differences from the App Router examples above:

  • API routes live in pages/api/ instead of src/app/api/
  • The handler receives req and res objects instead of a Request
  • Use res.revalidate() instead of importing revalidatePath from next/cache

Related resources

Was this page helpful?