@sanity/preview-url-secret
npm install @sanity/preview-url-secret @sanity/client
This package is used together with @sanity/presentation
:
import { presentationTool } from 'sanity/presentation'
import { defineConfig } from 'sanity'
export default defineConfig({
plugins: [
presentationTool({
previewUrl: {
origin: 'http://localhost:3000',
draftMode: {
enable: '/api/draft',
},
},
}),
],
})
Next.js App Router
Create an API token with viewer rights, and put it in an environment variable named SANITY_API_READ_TOKEN
, then create the following API handler:
import { draftMode } from 'next/headers'
import { redirect } from 'next/navigation'
import { validatePreviewUrl } from '@sanity/preview-url-secret'
import { client } from '@/sanity/lib/client'
const clientWithToken = client.withConfig({
token: process.env.SANITY_API_READ_TOKEN,
})
export async function GET(req: Request) {
const { isValid, redirectTo = '/' } = await validatePreviewUrl(
clientWithToken,
req.url,
)
if (!isValid) {
return new Response('Invalid secret', { status: 401 })
}
draftMode().enable()
redirect(redirectTo)
}
It's also handy to make a route to disable draft mode, so you have an easy way of disabling it when leaving the Presentation Mode and return to your app:
import { draftMode } from 'next/headers'
import { NextRequest, NextResponse } from 'next/server'
export function GET(request: NextRequest) {
draftMode().disable()
const url = new URL(request.nextUrl)
return NextResponse.redirect(new URL('/', url.origin))
}
Next.js Pages Router
Create an API token with viewer rights, and put it in an environment variable named SANITY_API_READ_TOKEN
, then create the following API handler:
import type { NextApiRequest, NextApiResponse } from 'next'
import { validatePreviewUrl } from '@sanity/preview-url-secret'
import { client } from '@/sanity/lib/client'
const clientWithToken = client.withConfig({
token: process.env.SANITY_API_READ_TOKEN,
})
export default async function handler(
req: NextApiRequest,
res: NextApiResponse<string | void>,
) {
if (!req.url) {
throw new Error('Missing url')
}
const { isValid, redirectTo = '/' } = await validatePreviewUrl(
clientWithToken,
req.url,
)
if (!isValid) {
return res.status(401).send('Invalid secret')
}
res.setDraftMode({ enable: true })
res.writeHead(307, { Location: redirectTo })
res.end()
}
It's also handy to make a route to disable draft mode, so you have an easy way of disabling it when leaving the Presentation Mode and return to your app:
import type { NextApiRequest, NextApiResponse } from 'next'
export default function handler(
_req: NextApiRequest,
res: NextApiResponse<void>,
): void {
res.setDraftMode({ enable: false })
res.writeHead(307, { Location: '/' })
res.end()
}
Debugging generated secrets
You can view the generated url secrets that are in your dataset by adding the debug plugin to your sanity.config.ts
:
import { defineConfig } from 'sanity'
import { debugSecrets } from '@sanity/preview-url-secret/sanity-plugin-debug-secrets'
export default defineConfig({
plugins: [
debugSecrets(),
],
})