Add live content to your application
Moves VisualEditing, defineLive, and isCorsOriginError exports to subpaths
v11.0.0
This major version update introduces changes to how applications must import VisualEditing, defineLive, and isCorsOriginError.
Breaking changes
This release moves the VisualEditing, defineLive, and isCorsOriginError exports to subpaths. This is necessary to allow support for output: static builds.
These changes are required for all projects updating to next-sanity v11 or later, regardless if they use static output builds.
Update steps
Update to the latest version of next-sanity:
npm install next-sanity@latestpnpm add next-sanity@latestAfter installing next-sanity v11 or higher, update any imports for VisualEditing, defineLive, and isCorsOriginError to use the associated paths instead of the root as shown in the examples below.
VisualEditing
import {VisualEditing} from 'next-sanity/visual-editing'
import {SanityLive} from '@/sanity/lib/live'
export default function RootLayout({children}: {children: React.ReactNode}) {
return (
<html lang="en">
<body>
{children}
<SanityLive />
{(await draftMode()).isEnabled && <VisualEditing />}
</body>
</html>
)
}import {VisualEditing} from 'next-sanity'
import {SanityLive} from '@/sanity/lib/live'
export default function RootLayout({children}: {children: React.ReactNode}) {
return (
<html lang="en">
<body>
{children}
<SanityLive />
{(await draftMode()).isEnabled && <VisualEditing />}
</body>
</html>
)
}defineLive
Import defineLive from the next-sanity/live path.
import {createClient} from 'next-sanity'
import {defineLive} from 'next-sanity/live'
const client = createClient({
projectId: process.env.NEXT_PUBLIC_SANITY_PROJECT_ID,
dataset: process.env.NEXT_PUBLIC_SANITY_DATASET,
useCdn: true,
apiVersion: 'v2025-03-04',
stega: {studioUrl: '/studio'},
})
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,
})import {createClient} from 'next-sanity'
import {defineLive} from 'next-sanity'
const client = createClient({
projectId: process.env.NEXT_PUBLIC_SANITY_PROJECT_ID,
dataset: process.env.NEXT_PUBLIC_SANITY_DATASET,
useCdn: true,
apiVersion: 'v2025-03-04',
stega: {studioUrl: '/studio'},
})
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,
})isCorsOriginError
Import isCorsOriginError from the next-sanity/live path.
'use client'
import {isCorsOriginError} from 'next-sanity/live'
import {toast} from 'sonner'
export function handleError(error: unknown) {
if (isCorsOriginError(error)) {
const {addOriginUrl} = error
toast.error(`Sanity Live couldn't connect`, {
description: `Your origin is blocked by CORS policy`,
duration: Infinity,
action: addOriginUrl
? {
label: 'Manage',
onClick: () => window.open(addOriginUrl.toString(), '_blank'),
}
: undefined,
})
} else if (error instanceof Error) {
console.error(error)
toast.error(error.name, {description: error.message, duration: Infinity})
} else {
console.error(error)
toast.error('Unknown error', {
description: 'Check the console for more details',
duration: Infinity,
})
}
}'use client'
import {isCorsOriginError} from 'next-sanity'
import {toast} from 'sonner'
export function handleError(error: unknown) {
if (isCorsOriginError(error)) {
const {addOriginUrl} = error
toast.error(`Sanity Live couldn't connect`, {
description: `Your origin is blocked by CORS policy`,
duration: Infinity,
action: addOriginUrl
? {
label: 'Manage',
onClick: () => window.open(addOriginUrl.toString(), '_blank'),
}
: undefined,
})
} else if (error instanceof Error) {
console.error(error)
toast.error(error.name, {description: error.message, duration: Infinity})
} else {
console.error(error)
toast.error('Unknown error', {
description: 'Check the console for more details',
duration: Infinity,
})
}
}