
Grab your gear: The official Sanity swag store
Read Grab your gear: The official Sanity swag storeI totally understand the frustration with authenticating draft mode! The good news is that the @sanity/preview-url-secret package is specifically designed to solve this problem. Let me walk you through how to secure it so only users with write access can enable draft mode.
@sanity/preview-url-secretThis package creates a secure workflow where:
Here's how to set it up for Next.js 13 App Router:
npm install @sanity/preview-url-secret @sanity/clientIn your sanity.config.ts:
import { defineConfig } from 'sanity'
import { presentationTool } from 'sanity/presentation'
export default defineConfig({
// ... other config
plugins: [
presentationTool({
previewUrl: {
origin: 'http://localhost:3000', // your Next.js URL
previewMode: {
enable: '/api/draft', // the API route we'll create
},
},
}),
],
})Create a Sanity API token with Viewer rights (not Editor!). Add it to your .env.local:
SANITY_API_READ_TOKEN=your_token_here
Create app/api/draft/route.ts:
import { client } from '@/sanity/lib/client'
import { validatePreviewUrl } from '@sanity/preview-url-secret'
import { draftMode } from 'next/headers'
import { redirect } from 'next/navigation'
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)
}Create app/api/disable-draft/route.ts:
import { draftMode } from 'next/headers'
import { NextRequest, NextResponse } from 'next/server'
export function GET(request: NextRequest) {
draftMode().disable()
return NextResponse.redirect(new URL('/', request.nextUrl.origin))
}The magic is in validatePreviewUrl(). Here's what it does according to the @sanity/preview-url-secret documentation:
sanity.previewUrlSecret)So effectively, only Sanity users with write access can generate valid preview URLs.
If you're on an Enterprise plan and want even more control, you can create custom roles that restrict access to preview secrets. The preview-url-secret reference explains how to:
If it's still not working, add the debug plugin to see the secrets being created:
import { debugSecrets } from '@sanity/preview-url-secret/sanity-plugin-debug-secrets'
export default defineConfig({
plugins: [
debugSecrets(), // View secrets in Studio
// ... other plugins
],
})The key insight is that @sanity/preview-url-secret ties authentication to Sanity's built-in permission system rather than requiring you to build your own auth layer. Users without write access simply can't generate the secrets needed to enable draft mode.
For more details on the complete setup, check out the Visual Editing with Next.js App Router guide and the Draft Mode documentation.
Hope this helps! Let me know if you run into any issues with the setup.
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.
Content operations
Content backend


The only platform powering content operations
By Industry


Tecovas strengthens their customer connections
Build and Share

Grab your gear: The official Sanity swag store
Read Grab your gear: The official Sanity swag store