Configuring the Sanity client for Next.js
Reference for Sanity client configuration in Next.js. Covers base config, environment setup, useCdn, feature layers, token handling, and per-request overrides.
The Sanity client is the foundation for all data fetching in a Next.js + Sanity application. This reference covers the base setup and explains what each feature layer adds to it. See the Sanity and Next.js introduction for installation and getting started details.
Base configuration
The minimal client for fetching published content:
import { createClient } from 'next-sanity'
import { apiVersion, dataset, projectId } from '../env'
export const client = createClient({
projectId,
dataset,
apiVersion,
useCdn: true,
})This is enough for basic content fetching in Server Components. The following sections explain each option and how to extend this configuration.
Environment variables
Centralize your project configuration in an env.ts file:
function assertValue<T>(v: T | undefined, errorMessage: string): T {
if (v === undefined) {
throw new Error(errorMessage)
}
return v
}
export const dataset = assertValue(
process.env.NEXT_PUBLIC_SANITY_DATASET,
'Missing environment variable: NEXT_PUBLIC_SANITY_DATASET'
)
export const projectId = assertValue(
process.env.NEXT_PUBLIC_SANITY_PROJECT_ID,
'Missing environment variable: NEXT_PUBLIC_SANITY_PROJECT_ID'
)
export const apiVersion =
process.env.NEXT_PUBLIC_SANITY_API_VERSION || '2026-03-01'The NEXT_PUBLIC_ prefix makes these values available in client-side code. This is safe because project ID and dataset name are not sensitive. They're visible in any API request your frontend makes.
The assertValue helper fails fast with a clear error if an environment variable is missing. This catches misconfiguration at startup instead of producing cryptic errors at runtime.
Set these values in .env.local for local development and in your hosting provider's environment for production. See Sanity and Next.js to get started.
Choose between the API CDN and the live API
Sanity offers a CDN for read queries that caches API responses at the edge. Whether to use it depends on your fetching context:
| Scenario | useCdn | Why |
|---|---|---|
| Client-side fetches (useEffect, user interactions) | true | Fast cached responses, reduces API load |
| High-traffic SSR (for example, personalized feeds) | true | Prevents API rate limiting under load |
| Static generation (generateStaticParams) | false | Need guaranteed-fresh data at build time |
| Webhook-triggered revalidation (ISR or tag-based) | false | Revalidated pages must fetch the latest content, not a cached response |
| Preview or Draft Mode | false | Must see unpublished drafts |
When in doubt, start with useCdn: true and switch to false for specific use cases using per-request overrides. This matrix applies to requests you make with client.fetch. If you fetch with sanityFetch from defineLive, it sets useCdn for each request from the perspective: the published perspective uses the CDN, and other perspectives, such as drafts, bypass it.
Feature layers
The base configuration works for basic fetching. When you enable additional features, the client configuration grows. Here's what each feature adds and why.
Visual Editing (stega overlays)
To connect click-to-edit overlays in the Presentation Tool to your studio, set the stega.studioUrl option:
import { createClient } from 'next-sanity'
import { apiVersion, dataset, projectId } from '../env'
export const client = createClient({
projectId,
dataset,
apiVersion,
useCdn: true,
stega: {
studioUrl: '/studio',
},
})The stega.studioUrl option tells the overlays where to open the editor. It doesn't encode anything on its own. During Draft Mode, sanityFetch from defineLive encodes invisible characters into string fields when a serverToken is configured. The overlays use these characters to map rendered text back to its source document and field.
Never allow stega in metadata
When generating metadata for SEO (generateMetadata, generateSitemaps), use stega: false or clean the data with stegaClean() from @sanity/client/stega. Invisible characters in title or meta tags will break search indexing.
See Visual Editing with Next.js App Router for the full setup.
Live Content API
To enable real-time content updates and automatic cache revalidation, configure defineLive with a token:
import { defineLive } from 'next-sanity/live'
import { client } from './client'
const token = process.env.SANITY_API_READ_TOKEN
if (!token) {
throw new Error('Missing SANITY_API_READ_TOKEN')
}
export const { sanityFetch, SanityLive } = defineLive({
client,
serverToken: token,
browserToken: token,
})defineLive takes your base client and returns a sanityFetch function that handles caching and revalidation automatically, plus a <SanityLive /> component that listens for real-time updates.
The token needs Viewer role permissions to fetch draft content. The same token can be used for both serverToken and browserToken. By default, <SanityLive /> sends the browserToken to the browser only when Draft Mode is on. Use a Viewer token, because anyone in Draft Mode can read it.
See the Live Content guide or the Visual Editing guide for the full setup including <SanityLive /> placement and Draft Mode configuration.
Recommended production setup (both features)
For a production application with both Visual Editing and Live Content, use src/sanity/lib/client.ts with the stega option and src/sanity/lib/live.ts with defineLive. Both files appear in full in the "Visual Editing (stega overlays)" and "Live Content API" sections.
Per-request overrides
Use client.withConfig() to create a client variant with different options for specific use cases:
import { client } from '@/sanity/lib/client'
// Bypass CDN for static generation
const freshClient = client.withConfig({ useCdn: false })
// Add a token for authenticated server-side requests
const authClient = client.withConfig({
token: process.env.SANITY_API_READ_TOKEN,
})
// Disable stega for metadata generation
const metadataClient = client.withConfig({ stega: false })withConfig() returns a new client instance. The original client is unchanged.
Next steps
- Query content with the
next-sanityclient. - Live Content guide: full
defineLivesetup withSanityLiveand Draft Mode. - Visual Editing with Next.js: complete Visual Editing setup for App Router.
- next-sanity reference documentation.