Настройка Sanity Visual Editing / Presentation
Visual Editing — режим редактирования контента прямо на сайте. Редактор видит сайт в iframe, кликает на элемент, открывается поле в Sanity Studio. Sanity Presentation Tool — официальный плагин для этого. Работает через Content Source Maps — Sanity добавляет к каждому фрагменту текста метаданные об его источнике.
Установка
npm install @sanity/presentation # в Sanity Studio
npm install @sanity/visual-editing # в Next.js
Настройка Studio
// sanity.config.ts
import { presentationTool } from '@sanity/presentation'
export default defineConfig({
plugins: [
structureTool(),
presentationTool({
previewUrl: {
previewMode: {
enable: '/api/draft-mode/enable',
disable: '/api/draft-mode/disable',
},
origin: process.env.SANITY_STUDIO_PREVIEW_URL || 'http://localhost:3000',
},
}),
],
})
Next.js: Draft Mode и Visual Editing
// app/api/draft-mode/enable/route.ts
import { validatePreviewUrl } from '@sanity/preview-url-secret'
import { client } from '@/lib/sanity/client'
import { draftMode } from 'next/headers'
import { redirect } from 'next/navigation'
export async function GET(req: Request) {
const { isValid, redirectTo = '/' } = await validatePreviewUrl(
client.withConfig({ token: process.env.SANITY_API_TOKEN! }),
req.url
)
if (!isValid) {
return new Response('Invalid secret', { status: 401 })
}
draftMode().enable()
redirect(redirectTo)
}
// app/api/draft-mode/disable/route.ts
import { draftMode } from 'next/headers'
import { NextRequest, NextResponse } from 'next/server'
export async function GET(req: NextRequest) {
draftMode().disable()
return NextResponse.redirect(new URL('/', req.url))
}
// components/VisualEditingProvider.tsx
'use client'
import { enableVisualEditing } from '@sanity/visual-editing/next-pages-router'
import { useEffect } from 'react'
export function VisualEditingProvider() {
useEffect(() => {
return enableVisualEditing()
}, [])
return null
}
// app/layout.tsx
import { draftMode } from 'next/headers'
import { VisualEditingProvider } from '@/components/VisualEditingProvider'
export default function RootLayout({ children }: { children: React.ReactNode }) {
const { isEnabled } = draftMode()
return (
<html>
<body>
{children}
{isEnabled && <VisualEditingProvider />}
</body>
</html>
)
}
Content Source Maps
Content Source Maps — Sanity добавляет к ответу API метаданные, позволяющие связать текст на странице с конкретным полем в Studio:
// Запрос с stega-кодированием (для visual editing)
import { createClient } from 'next-sanity'
const client = createClient({
projectId: process.env.NEXT_PUBLIC_SANITY_PROJECT_ID!,
dataset: 'production',
apiVersion: '2024-01-01',
useCdn: false,
stega: {
enabled: true, // включить Content Source Maps
studioUrl: process.env.NEXT_PUBLIC_SANITY_STUDIO_URL!,
},
})
Когда stega: true, Sanity встраивает в строки невидимые символы с метаданными. Visual Editing читает их и знает, какое поле Studio открыть при клике на элемент.
Live Preview через useOptimistic
// app/(preview)/posts/[slug]/page.tsx
import { sanityFetch } from '@/lib/sanity/live' // fetch с live-обновлениями
export default async function PreviewPostPage({ params }: { params: { slug: string } }) {
const { data: post } = await sanityFetch({
query: `*[_type == "post" && slug.current == $slug][0] { title, body }`,
params: { slug: params.slug },
})
return <Article post={post} />
}
// lib/sanity/live.ts
import { createClient, defineLive } from 'next-sanity'
const client = createClient({
projectId: process.env.NEXT_PUBLIC_SANITY_PROJECT_ID!,
dataset: 'production',
apiVersion: '2024-01-01',
useCdn: false,
token: process.env.SANITY_API_TOKEN,
})
export const { sanityFetch, SanityLive } = defineLive({ client })
// app/layout.tsx — добавить SanityLive для real-time обновлений
import { SanityLive } from '@/lib/sanity/live'
export default function RootLayout({ children }) {
return (
<html>
<body>
{children}
<SanityLive /> {/* компонент обновляет данные через WebSocket */}
</body>
</html>
)
}
Сроки
Настройка Visual Editing с Presentation Tool и Draft Mode — 1–2 дня.







