Sanity CMS + Next.js — od instalacji po live preview i Visual Editing
Jak zintegrować Sanity CMS z Next.js App Router? Schema, GROQ queries, ISR, live preview, Visual Editing i deploy — kompletny setup headless CMS dla strony usługowej.
Dla stron usługowych w Next.js Sanity rozwiązuje konkretny problem, który początkowo może nie być taki oczywisty. Nietechniczny klient może samodzielnie edytować treści - teksty, zdjęcia, meta tagi, FAQ to sekcja najczęściej zadawanych pytań i odpowiedzi, często używana też do danych strukturalnych. - i to bez angażowania dewelopera przy każdej zmianie.
Setup
Code
# Inicjalizacja Sanity w projekcie Next.jsnpm create sanity@latest -- --project-id xxx --dataset production --template cleannpm install next-sanity @sanity/image-url @portabletext/react
Oczywiście możesz też użyć yarn zamiast npm.
Sanity Studio - czyli panel admina - działa jako route w Twojej aplikacji Next.js, czyli wchodzisz na /studio.
SANITY_API_READ_TOKEN generujesz w manage.sanity.io → API, czyli Application Programming Interface, definiuje sposób komunikacji między aplikacjami lub modułami. Tu chodzi o konwencje plików Next.js, które zamieniają eksportowaną funkcję w gotowy plik sitemap.xml lub robots.txt. → Tokens → dodaj token z uprawnieniami Viewer.
Przy Draft Mode w Next.js pozwala tymczasowo ominąć cache i renderować nieopublikowane lub robocze treści., preview i Visual Editing używaj previewClient — widzi najnowsze drafty zamiast danych z CDN, czyli Content Delivery Network, to rozproszona sieć serwerów dostarczająca zasoby z węzła najbliższego użytkownikowi; CDN do obrazów dodatkowo transformuje je w locie..
Pomocnik urlFor
Code
// lib/sanity/image.tsimport imageUrlBuilder from '@sanity/image-url'import { client } from './client'const builder = imageUrlBuilder(client)export function urlFor(source: Parameters<typeof builder.image>[0]) { return builder.image(source)}
Schema — definicja typów treści
Najpierw blockContent — typ wielokrotnego użytku dla pól Portable Text:
Wygeneruj typy TypeScript ze schemy, żeby uniknąć any w komponentach:
Code
npx sanity typegen generate
Tworzy sanity.types.ts — importuj stamtąd typy zamiast pisać je ręcznie.
Zapytania GROQ
GROQ to język zapytań Sanity służący do pobierania i filtrowania treści z datasetu. to język zapytań Sanity — podobny do GraphQL, ale
prostszy. Każde zapytanie powinno deklarować cache tagi, żeby on-demand
revalidation działała poprawnie:
Zamiast czekać na rewalidację czasową — odśwież stronę natychmiast po edycji w Sanity:
Code
// app/api/revalidate/route.tsimport { revalidateTag } from 'next/cache'import { NextRequest, NextResponse } from 'next/server'import { parseBody } from 'next-sanity/webhook'export async function POST(req: NextRequest) { try { const { isValidSignature, body } = await parseBody( req, process.env.SANITY_WEBHOOK_SECRET, ) if (!isValidSignature) { return NextResponse.json({ error: 'Invalid signature' }, { status: 401 }) } const { _type, slug } = body if (_type === 'post') { revalidateTag('posts') if (slug?.current) revalidateTag(`post-${slug.current}`) } return NextResponse.json({ revalidated: true }) } catch (error) { return NextResponse.json({ error: 'Bad Request' }, { status: 400 }) }}
W panelu Sanity: Manage → API → Webhooks → dodaj URL (https://twoja-domena.pl/api/revalidate) i secret.
Draft Mode — włączanie podglądu
Draft Mode to mechanizm Next.js przełączający rendering z cache'owanych danych na dane na żywo — czyli drafty z Sanity. Potrzebujesz dwóch route'ów: jeden włącza podgląd, drugi go wyłącza:
Code
// app/api/draft/enable/route.tsimport { draftMode } from 'next/headers'import { redirect } from 'next/navigation'import { NextRequest } from 'next/server'export async function GET(request: NextRequest) { const { searchParams } = new URL(request.url) if (searchParams.get('secret') !== process.env.SANITY_PREVIEW_SECRET) { return new Response('Invalid token', { status: 401 }) } const draft = await draftMode() draft.enable() redirect(searchParams.get('redirect') || '/')}
Code
// app/api/draft/disable/route.tsimport { draftMode } from 'next/headers'import { redirect } from 'next/navigation'export async function GET() { const draft = await draftMode() draft.disable() redirect('/')}
W Sanity Studio skonfiguruj Presentation Tool z URL podglądu:
previewClient ma perspective: 'previewDrafts' — zwraca nieopublikowane zmiany. Dla pełnego preview na żywo z aktualizacjami w czasie rzeczywistym (bez przeładowania strony) użyj VisualEditing razem z @sanity/preview-kit — dane są wtedy synchronizowane przez WebSocket w momencie każdej zmiany w edytorze.
Visual Editing — edycja treści na żywej stronie
Visual Editing pozwala klientowi kliknąć dowolny element na żywej stronie i edytować go bez otwierania panelu Studio:
Klient widzi stronę jak użytkownik, ale edytowalne elementy dostają overlay prowadzący do Sanity Studio lub Presentation Tool. Jeśli Studio jest osadzone w tej samej aplikacji, trzymaj jego route w osobnej grupie layoutów i nie renderuj VisualEditing nad panelem Studio.
Często zadawane pytania
O autorze
Maciej Sala
Maciej Sala — project manager i frontendowiec z doświadczeniem w marketingu internetowym. Na co dzień pracuję z Reactem, Next.js i TypeScriptem, łącząc perspektywę produktową z praktycznym podejściem do kodu. Przez kilka lat związany z branżą gier wideo jako project manager i game designer.
Absolwent historii na Uniwersytecie Jagiellońskim i studiów podyplomowych z marketingu internetowego na Akademii Górniczo-Hutniczej w Krakowie. Poza pracą trenuje na siłowni, maluje figurki i realizuje własne projekty.
Programmatic SEO w połączeniu z AI i Next.js ISR/SSG pozwala skalować produkcję treści bez proporcjonalnego wzrostu kosztów. Praktyczny przewodnik po architekturze, generowaniu treści i optymalizacji pod Google, ChatGPT i Perplexity.
Next.js vs WordPress w 2026 — obiektywne porównanie dla firm, freelancerów i agencji. Wydajność, SEO, bezpieczeństwo, koszty, łatwość edycji — kiedy który wybrać i dlaczego.
Jak poprawnie wdrożyć GA4 w Next.js App Router: gtag, @next/third-parties, page_view przy client-side navigation, consent mode v2 i custom events bez chaosu w danych.