Интеграция Sanity CMS с Next.js, Nuxt и Remix — ISR, preview, webhooks под ключ.
Полная интеграция Sanity с современными React-фреймворками
При разработке на Next.js часто возникает проблема гидратации контента из Sanity, а также необходимость реализовать preview mode для редакторов. К тому же, без ISR каждая публикация требует полной пересборки — это увеличивает TTFB на 30–50%. Мы поможем настроить интеграцию под ключ: с ISR, webhooks и типизацией. Наш опыт — 10+ проектов с Sanity, 5 лет на рынке. Свяжитесь, чтобы получить консультацию и оценку вашего проекта.
Sanity работает через HTTP API — любой фреймворк может читать контент. Официальные библиотеки: next-sanity для Next.js, @sanity/client для остальных. Благодаря CDN Sanity Client отвечает в среднем за 50–80 мс, что в 2–3 раза быстрее fetch-запросов к обычному REST API. Официальная документация Sanity
Преимущества Sanity с серверными фреймворками
Sanity — headless CMS с реальным временем: редакторы видят изменения мгновенно. В паре с Next.js или Nuxt вы получаете статические страницы, которые обновляются только при изменении контента. Это снижает нагрузку на сервер до 40% и улучшает LCP. Кроме того, GROQ-запросы позволяют выбирать только нужные поля — объём передаваемых данных сокращается на 60–70%.
Подключение Sanity к Next.js через App Router
Установите пакеты: npm install next-sanity @sanity/client. Настройте клиент. Для черновиков используйте preview client c токеном.
// lib/sanity/client.ts
import { createClient } from 'next-sanity'
export const client = createClient({
projectId: process.env.NEXT_PUBLIC_SANITY_PROJECT_ID!,
dataset: process.env.NEXT_PUBLIC_SANITY_DATASET || 'production',
apiVersion: process.env.SANITY_API_VERSION, // установите актуальную версию
useCdn: process.env.NODE_ENV === 'production',
})
export const previewClient = createClient({
projectId: process.env.NEXT_PUBLIC_SANITY_PROJECT_ID!,
dataset: process.env.NEXT_PUBLIC_SANITY_DATASET || 'production',
apiVersion: process.env.SANITY_API_VERSION,
useCdn: false,
token: process.env.SANITY_API_TOKEN,
perspective: 'previewDrafts',
})В серверных компонентах используйте client.fetch с GROQ-запросами. Пример страницы поста:
// app/posts/[slug]/page.tsx
import { client, previewClient } from '@/lib/sanity/client'
import { groq } from 'next-sanity'
import { draftMode } from 'next/headers'
const query = groq`*[_type == "post" && slug.current == $slug][0] {
_id,
title,
body,
publishedAt,
"author": author->{ name },
"slug": slug.current
}`
export default async function PostPage({ params }: { params: { slug: string } }) {
const { isEnabled } = draftMode()
const activeClient = isEnabled ? previewClient : client
const post = await activeClient.fetch(query, { slug: params.slug }, { next: { tags: [`post-${params.slug}`] } })
if (!post) notFound()
return <Article post={post} />
}
export async function generateStaticParams() {
const slugs = await client.fetch<string[]>(`*[_type == "post"].slug.current`)
return slugs.filter(Boolean).map(slug => ({ slug }))
} On-demand Revalidation через ISR и webhooks
ISR обновляет страницы только когда контент изменился — это снижает нагрузку на сервер. Создайте webhook в Sanity и эндпоинт, который вызывает revalidateTag. Пример:
// app/api/revalidate/route.ts
import { revalidateTag } from 'next/cache'
import { parseBody } from 'next-sanity/webhook'
export async function POST(req: Request) {
try {
const { isValidSignature, body } = await parseBody<{ _type: string; slug?: { current: string } }>(
req,
process.env.SANITY_WEBHOOK_SECRET!
)
if (!isValidSignature) {
return Response.json({ message: 'Invalid signature' }, { status: 401 })
}
if (body._type === 'post') {
revalidateTag('posts')
if (body.slug?.current) {
revalidateTag(`post-${body.slug.current}`)
}
}
return Response.json({ revalidated: true })
} catch (err) {
return Response.json({ message: 'Invalid request' }, { status: 400 })
}
} Интеграция с Nuxt 3
Установите модуль @nuxtjs/sanity и добавьте конфигурацию в nuxt.config.ts.
// nuxt.config.ts
export default defineNuxtConfig({
modules: ['@nuxtjs/sanity'],
sanity: {
projectId: process.env.NUXT_PUBLIC_SANITY_PROJECT_ID,
dataset: 'production',
apiVersion: process.env.SANITY_API_VERSION,
useCdn: process.env.NODE_ENV === 'production',
},
})В компоненте используйте $sanity для fetch-запросов:
<template>
<article v-if="post">
<h1>{{ post.title }}</h1>
<SanityContent :blocks="post.body" />
</article>
</template>
<script setup lang="ts">
const { $sanity } = useNuxtApp()
const route = useRoute()
const { data: post } = await useAsyncData(`post-${route.params.slug}`, () =>
$sanity.fetch(
`*[_type == "post" && slug.current == $slug][0] { title, body }`,
{ slug: route.params.slug }
)
)
</script> Интеграция Sanity с Remix
В Remix данные загружаются в loader-функциях на сервере — страница рендерится уже с контентом, без hydration-задержек и клиентских waterfall-запросов. Используйте @sanity/client напрямую в loader: client.fetch(query, { slug: params.slug }), затем передайте данные в компонент через useLoaderData.
Remix не поддерживает ISR из коробки. Для кеширования используйте HTTP-заголовки Cache-Control: s-maxage=3600, stale-while-revalidate=86400 в loader — Cloudflare и Vercel Edge подхватывают их автоматически. Webhook от Sanity инвалидирует кеш через Cloudflare Purge API при публикации нового контента. Среднее время ответа при правильно настроенном CDN — 60–100 мс.
Типизация контента через Sanity TypeGen
Команда npx sanity@latest typegen generate создаёт TypeScript-типы из схемы студии. GROQ-запросы становятся типобезопасными: IDE подсказывает поля, TypeScript ловит опечатки на этапе сборки. Это экономит 1–2 часа на каждом новом типе контента и снижает число ошибок в production.
Сравнение фреймворков: скорость работы с Sanity Client
| Фреймворк | Время первого ответа (TTFB) | Простота настройки | Документация |
|---|---|---|---|
| Next.js | Низкое (ISR/SSR) | Высокая | Отличная |
| Nuxt 3 | Среднее (SSR/SSG) | Высокая | Хорошая |
| Remix | Низкое (loader) | Средняя | Хорошая |
На практике Sanity Client быстрее fetch-запросов в 2–3 раза за счёт CDN и эффективного кэширования.
Типичные ошибки при интеграции
-
Hydration mismatch из-за несовпадения данных на сервере и клиенте — решается синхронизацией запросов через
draftMode. -
CORS errors при запросах из браузера — настройте
CORS originsв панели Sanity. -
N+1 запросы при вложенных ссылках — используйте GROQ-оператор
->для подгрузки связанных документов. - Устаревший кэш при ISR — убедитесь, что webhook секрет корректен и эндпоинт доступен.
Что входит в работу
- Настройка Sanity Client и конфигурация API-версии
- Реализация preview mode (draft mode) с токенами
- Webhook для on-demand revalidation
- GROQ-запросы и типизация контента (generated types)
- Оптимизация изображений через Sanity Image CDN
- Документация и обучение команды
Как строится процесс работы
- Аналитика: обсуждаем архитектуру контента и запросов
- Проектирование: схемы GROQ-запросов, типы
- Реализация: интеграция клиента, страницы, preview
- Тестирование: проверка гидратации, ошибок ревалидации
- Деплой: настройка переменных окружения, webhook
Сроки и стоимость
| Пакет | Что включено | Срок |
|---|---|---|
| Базовый | Next.js + ISR + Webhooks | 1–2 дня |
| Расширенный | +Nuxt или Remix, preview mode | 2–3 дня |
| Под ключ | Все фреймворки + TypeGen + документация | 3–5 дней |
Стоимость базовой интеграции Next.js с ISR — от $140–200, полный пакет под ключ — от $360–520. Ориентировочное время: Next.js App Router — 1–2 дня, Nuxt 3 — 1 день, Remix — 1 день, все 3 фреймворка — 3–5 дней. Закажите интеграцию — мы подготовим точную оценку. Получите консультацию: наши инженеры помогут выбрать оптимальную архитектуру.







