
Przewodnik po Sanity CMS: Jak publikujemy w 10 językach
Opublikowaliśmy ponad 400 artykułów na 4 stronach internetowych w 10 językach za pośrednictwem Sanity CMS. Oto czego się nauczyliśmy – od projektowania schematów po automatyczną publikację wielojęzyczną.
Sanity CMS to platforma typu headless zbudowana wokół ustrukturyzowanej treści, działającego w czasie rzeczywistym Content Lake oraz edytora opartego na React o nazwie Sanity Studio, który można dowolnie dostosowywać. Wykorzystuje GROQ do odpytywania, Portable Text do bogatej treści oraz podejście „schema-as-code” (schemat jako kod) do modelowania treści. Ten przewodnik obejmuje konfigurację, projektowanie schematów, GROQ, Portable Text, architekturę wielojęzyczną oraz cennik.
Czym jest Sanity CMS?
Sanity to platforma do ustrukturyzowanej treści, którą zespół Sanity.io nazywa „systemem operacyjnym dla treści”. W przeciwieństwie do tradycyjnych systemów CMS, które przechowują bloki HTML w bazie danych, Sanity zapisuje każdy fragment treści jako ustrukturyzowany JSON w zarządzanym backendzie zwanym Content Lake. Odpytujesz go za pomocą GROQ lub GraphQL i renderujesz treść w dowolnym froncendzie: Next.js, React Native, Svelte, aplikacji mobilnej, narzędziu CLI – czymkolwiek zechcesz.
Firmy korzystające z tego rozwiązania reprezentują cały спектр. Nike, Figma, Puma i Cloudflare używają Sanity w skali enterprise. Startupy wybierają je, ponieważ darmowy plan jest naprawdę użyteczny (więcej o cenach później). My używamy go, ponieważ żadne inne rozwiązanie nie dało nam elastyczności potrzebnej do zbudowania w pełni zautomatyzowanego potoku publikacji w 10 językach.
Architektura Content Lake
Content Lake to zarządzany backend Sanity. Można o nim myśleć jako o hostowanym magazynie dokumentów, który synchronizuje się w czasie rzeczywistym ze wszystkimi podłączonymi klientami. Gdy redaktor zmienia akapit w Sanity Studio, inny redaktor widzi tę zmianę natychmiast – bez przycisku zapisu, bez konfliktów scalania, bez migracji bazy danych.
Pod maską dokumenty są przechowywane jako ustrukturyzowany JSON z typowanymi polami. Każda mutacja jest śledzona przez dziennik transakcji, więc domyślnie otrzymujesz pełną historię wersji. Synchronizacja w czasie rzeczywistym wykorzystuje architekturę opartą na listenerach (opisaną w dokumentacji architektury Sanity na GitHubie), która wypycha zmiany do wszystkich subskrybentów za pomocą obserwabli RxJS.
Czym różni się to np. od bazy danych PostgreSQL z API REST? Content Lake obsługuje modelowanie treści, kontrolę dostępu, buforowanie CDN, transformacje obrazów i współpracę w czasie rzeczywistym jako jedną zarządzaną usługę. Nie uruchamiasz migracji. Nie zarządzasz replikami. Po prostu definiujesz schematy i odpytujesz treści.
Sanity Studio: Twój konfigurowalny edytor
Sanity Studio to aplikacja open-source napisana w React, która służy jako interfejs edycji. Nie jest to hostowany panel administracyjny, lecz aplikacja React, która żyje w Twoim repozytorium kodu. Możesz dostosować każdy jej aspekt: niestandardowe komponenty wejściowe, pola warunkowe, akcje dokumentów, wzorce Structure Buildera oraz wtyczki.
Współpraca w czasie rzeczywistym jest wbudowana. Wielu redaktorów może pracować nad tym samym dokumentem jednocześnie, widząc wskaźniki obecności i aktualizacje na żywo. Jeśli korzystałeś z Google Docs, doświadczenie jest podobne – widzisz kursory innych osób i ich zmiany w czasie rzeczywistym.
Wdrażamy nasze Studio za pomocą npx sanity deploy, co hostuje je w CDN Sanity pod niestandardową subdomeną. Możesz też hostować je samodzielnie, ponieważ to po prostu aplikacja React. Wysoko oceniliśmy Sanity w naszym porównaniu systemów headless CMS głównie ze względu na elastyczność Studio.
Jak skonfigurować projekt Sanity
Aby skonfigurować Sanity CMS, zainstaluj CLI poleceniem npm create sanity@latest, wybierz szablon projektu, skonfiguruj pliki schematu i uruchom npx sanity dev, aby lokalnie uruchomić Studio. Cały proces zajmuje mniej niż 5 minut.
Wymagania wstępne i instalacja
Potrzebujesz Node.js 18+ oraz npm (lub pnpm). To wszystko. Uruchom polecenie inicjalizujące:
npm create sanity@latest
# You'll be prompted for:
# - Login method (Google, GitHub, email)
# - Project name
# - Dataset name (default: "production")
# - Project template (blog, ecommerce, clean)
# - TypeScript? (recommended: yes)CLI szkieletuje projekt ze wszystkim, czego potrzebujesz. Oto jak wygląda struktura projektu:
Wyjaśnienie struktury projektu
my-sanity-project/
├── schemas/ # Your content schemas (this is where you'll spend time)
│ ├── index.ts # Schema registry -- imports and exports all types
│ ├── post.ts # Document type definitions
│ └── blockContent.ts # Rich text / Portable Text config
├── sanity.config.ts # Main config -- plugins, Studio structure, dataset
├── sanity.cli.ts # CLI config -- project ID, dataset
├── package.json
└── tsconfig.jsonPlik sanity.config.ts jest punktem wejścia. Oto minimalna konfiguracja:
// sanity.config.ts
import { defineConfig } from 'sanity'
import { structureTool } from 'sanity/structure'
import { visionTool } from '@sanity/vision'
import { schemaTypes } from './schemas'
export default defineConfig({
name: 'default',
title: 'My Blog',
projectId: 'your-project-id',
dataset: 'production',
plugins: [structureTool(), visionTool()],
schema: { types: schemaTypes },
})Wtyczka visionTool() daje Ci plac zabaw GROQ bezpośrednio w Studio – będziesz z niej często korzystać podczas rozwoju.
Wdrażanie Studio
Uruchom lokalnie za pomocą npx sanity dev (działa na localhost:3333). Kiedy będziesz gotowy do udostępnienia go redaktorom, wdróż go w CDN Sanity:
npx sanity deploy
# Prompts for a hostname, e.g., "my-blog"
# Deploys to https://my-blog.sanity.studioWskazówka pro: uruchom npx sanity@latest schema deploy po każdej zmianie schematu. To przesyła Twój schemat do API Sanity, co umożliwia funkcje takie jak API GraphQL i narzędzia świadome schematu (w tym serwer MCP, o którym wspomnimy później).
Projektowanie schematów w Sanity CMS
Schematy Sanity są definiowane jako obiekty JavaScript lub TypeScript w Twoim kodzie. Każdy schemat określa typ dokumentu z polami, regułami walidacji i niestandardowymi komponentami wejściowymi. Zmiany w schematach są natychmiastowe – nie wymagają migracji bazy danych. To podejście „schema-as-code” przekonało nas do Sanity bardziej niż do Contentful.
Typy pól i walidacja
Sanity oferuje bogaty zestaw typów pól. Oto te, których używamy najczęściej:
| Typ pola | Przypadek użycia | Przykład |
|---|---|---|
string | Krótki tekst, tytuły, slugi | Tytuł posta, imię autora |
text | Wieloliniowy zwykły tekst | Wstępy, opisy |
number | Liczby całkowite, zmiennoprzecinkowe | Czas czytania, kolejność sortowania |
boolean | Przełączniki | Flaga „wyróżnione”, status szkicu |
array | Listy, bogaty tekst (Portable Text) | Treść główna, tagi |
reference | Linki do innych dokumentów | Autor, kategoria |
image | Obrazy z metadanymi | Obraz okładkowy z tekstem alternatywnym |
slug | Przyjazne dla URL ciągi znaków | Automatycznie generowane z tytułu |
object | Zagnieżdżone grupy pól | Pola SEO (metaTitle + metaDescription) |
date / datetime | Daty | Data publikacji |
Każde pole obsługuje walidację poprzez callback validation. Możesz wymuszać wymagane pola, wartości min/max, wzorce regex i niestandardowe reguły:
defineField({
name: 'seoDescription',
title: 'Meta Description',
type: 'string',
validation: (Rule) =>
Rule.required()
.min(145)
.max(160)
.warning('Meta description should be 145-160 characters'),
})Niestandardowe typy bloków (nasze przykłady produkcyjne)
Tutaj Sanity staje się interesujące i tam, gdzie 0 na 6 konkurencyjnych przewodników pokazuje jakikolwiek kod. W naszym produkcyjnym schemacie definiujemy pięć niestandardowych typów bloków wewnątrz tablicy body: block (standardowy tekst), table, codeBlock, chartBlock i inlineImage.
Oto nasza definicja codeBlock:
// schemas/objects/codeBlock.ts
import { defineType } from 'sanity'
export const codeBlock = defineType({
name: 'codeBlock',
title: 'Code Block',
type: 'object',
fields: [
{
name: 'language',
title: 'Language',
type: 'string',
options: {
list: [
{ title: 'JavaScript', value: 'javascript' },
{ title: 'TypeScript', value: 'typescript' },
{ title: 'Python', value: 'python' },
{ title: 'Bash', value: 'bash' },
{ title: 'JSON', value: 'json' },
{ title: 'GROQ', value: 'groq' },
],
},
},
{
name: 'code',
title: 'Code',
type: 'text',
},
],
})A oto jak pole body odwołuje się do wszystkich naszych niestandardowych typów razem:
// schemas/fields/body.ts
defineField({
name: 'body',
title: 'Body',
type: 'array',
of: [
{ type: 'block' }, // Standard Portable Text (paragraphs, headings, lists)
{ type: 'table' }, // @sanity/table plugin
{ type: 'codeBlock' }, // Our custom code block
{ type: 'chartBlock' }, // Data visualization (bar, line, pie)
{ type: 'inlineImage' }, // Images with alt text and captions
],
})Daje to naszym redaktorom bogaty zestaw narzędzi do tworzenia treści, jednocześnie utrzymując każdy element jako typowany i odpytywalny. chartBlock to nie tylko nieprzezroczysty osadzony HTML, ale ustrukturyzowane dane z polami chartType, title, dataPoints i dataLabels. Ma to znaczenie, gdy próbujesz renderować tę samą treść w internecie, e-mailu i na urządzeniach mobilnych.
Najlepsze praktyki organizacji schematów
Utrzymuj schematy modularne. My dzielimy je na pliki według typu: schemas/documents/post.ts, schemas/objects/codeBlock.ts, schemas/objects/chartBlock.ts. Importujemy je wszystkie w schemas/index.ts:
// schemas/index.ts
import { post } from './documents/post'
import { codeBlock } from './objects/codeBlock'
import { chartBlock } from './objects/chartBlock'
import { inlineImage } from './objects/inlineImage'
export const schemaTypes = [post, codeBlock, chartBlock, inlineImage]Kluczowa wiedza, którą zdobyliśmy pracując z ustrukturyzowaną treścią: Twój schemat TO JEST Twój model treści. Jeśli potraktujesz go jako inżynierię kontekstu dla swojego zespołu treściowego, podejmiesz lepsze decyzje projektowe. Każde dodane pole powinno mieć cel – czy to dla redaktorów, do renderowania, czy do odpytywania.
GROQ: Język zapytań Sanity
GROQ (Graph-Relational Object Queries) to otwartoźródłowy język zapytań Sanity do filtrowania, łączenia i projekcji dokumentów JSON. Podstawowa składnia to *[filter]{projection} – wybierz wszystkie dokumenty pasujące do filtra, a następnie ukształtuj wyjście. Jest bardziej zwięzły niż GraphQL w przypadku zapytań specyficznych dla Sanity i, według naszych doświadczeń, szybszy do nauki.
Podstawowe zapytania: Filtrowanie i projekcja
Najprostsze zapytanie pobiera wszystkie dokumenty danego typu:
// Fetch all posts -- just title and slug
*[_type == "post"]{
title,
"slug": slug.current
}
// Filter by language, expand author reference
*[_type == "post" && language == "en"]{
title,
"slug": slug.current,
"authorName": author->name,
"authorImage": author->image,
"categoryTitle": category->title,
publishedAt
}Operator -> podąża za referencjami. author->name oznacza „podążaj za referencją autora i zwróć pole name”. Brak osobnych zapytań, brak problemów N+1, brak JOIN-ów – wszystko w jednym wyrażeniu.
Łączenie, sortowanie i paginacja
Dla naszych stron indeksu bloga potrzebujemy posortowanych, stronicowanych postów z rozwiniętymi referencjami:
// Paginated posts with full metadata
*[_type == "post" && language == "en"] | order(publishedAt desc) [0...10] {
title,
"slug": slug.current,
excerpt,
publishedAt,
readTime,
"author": author->{name, image},
"category": category->{title, "slug": slug.current},
"coverImage": coverImage{
"src": asset->url,
alt
}
}[0...10] daje pierwsze 10 wyników (indeksowane od 0, koniec wyłączny). | order(publishedAt desc) sortuje od najnowszych. Projekcja kształtuje wyjście tak, aby zawierało dokładnie to, czego potrzebuje Twój frontend – nic więcej.
Możesz testować wszystkie te zapytania interaktywnie za pomocą wtyczki Vision wewnątrz Sanity Studio. Jest nieoceniona podczas rozwoju. Więcej wzorców znajdziesz w ściągawce GROQ.
GROQ vs GraphQL
Sanity obsługuje zarówno GROQ, jak i GraphQL. Kiedy używać którego?
GROQ to natywny język Sanity. Obsługuje joiny, projekcje i pola obliczeniowe w jednym ciągu zapytania. To właśnie pod kątem tego zoptymalizowano Content Lake.
GraphQL jest dostępny po wdrożeniu schematu (npx sanity@latest schema deploy). Używaj go, gdy potrzebujesz standaryzowanych narzędzi, np. jeśli Twój frontend już używa Apollo Client lub jeśli Twój zespół zna GraphQL, ale nie GROQ.
My używamy wyłącznie GROQ. Jest bardziej ekspresyjny dla danych Sanity, a wtyczka Vision sprawia, że debugowanie zapytań jest trywialne.
Portable Text: Bogata treść zrobiona dobrze
Portable Text to specyfikacja Sanity dla ustrukturyzowanego bogatego tekstu. Zamiast przechowywać treść jako ciągi HTML, przechowuje ona tablicę typowanych bloków – akapitów, nagłówków, obrazów, fragmentów kodu, tabel – każdy jako obiekt JSON. Dzięki temu treść można renderować w dowolnym frameworku, na dowolnej platformie, w dowolnym formacie.
Struktura danych
Oto jak akapit i blok kodu wyglądają jako JSON Portable Text:
[
{
"_type": "block",
"_key": "a1b2c3",
"style": "normal",
"markDefs": [],
"children": [
{
"_type": "span",
"_key": "d4e5f6",
"text": "Here's an example of our pipeline config:",
"marks": []
}
]
},
{
"_type": "codeBlock",
"_key": "g7h8i9",
"language": "typescript",
"code": "export default defineConfig({ ... })"
}
]Każdy blok ma _type i _key. Standardowe bloki tekstowe używają "block" z dziećmi spans (które obsługują znaczniki jak pogrubienie, kursywa i linki). Niestandardowe bloki, takie jak nasz codeBlock, chartBlock, table i inlineImage, używają własnego _type i przenoszą ustrukturyzowane pola.
Dlaczego to ważne? Ponieważ HTML to format renderowania, a nie przechowywania. Jeśli przechowujesz <h2>Tytuł</h2><p>Jakiś <strong>tekst</strong></p> w swojej bazie danych, zablokowałeś się na renderowaniu webowym. Nie możesz czysto wyodrębnić tego dla aplikacji mobilnej, newslettera e-mail, PDF-a lub okna kontekstowego agenta AI. Portable Text oddziela treść od prezentacji. Specyfikacja Portable Text jest open source – to nie jest lock-in Sanity.
Niestandardowe bloki w produkcji
Nasz potok konwertuje Markdown do Portable Text za pomocą skryptu Pythona (scripts/md_to_portable_text.py). Konwerter obsługuje standardowe bloki plus nasze cztery niestandardowe typy:
table, używa schematu wtyczki@sanity/table. Wiersze i komórki przechowywane jako ustrukturyzowane dane.codeBlock, język i kod jako osobne pola, umożliwiające podświetlanie składni podczas renderowania.chartBlock, typ wykresu, tytuł, etykiety osi, nazwy serii i punkty danych jako ustrukturyzowany JSON. Frontend renderuje je za pomocą Chart.js.inlineImage, tekst alternatywny, źródło i opcjonalny podpis jako osobne pola.
Ta struktura oznacza, że możemy odpytywać o wszystkie przykłady kodu w naszym blogu (*[body[]._type == "codeBlock"]), znaleźć posty z wykresami lub wyodrębnić wszystkie obrazy z brakującym tekstem alternatywnym – wszystko przez GROQ.
Renderowanie Portable Text
We froncie użyj @portabletext/react (lub odpowiedników dla Svelte/Vue). Rejestrujesz niestandardowe komponenty dla każdego typu bloku:
import { PortableText } from '@portabletext/react'
const components = {
types: {
codeBlock: ({ value }) => (
<pre className={`language-${value.language}`}>
<code>{value.code}</code>
</pre>
),
chartBlock: ({ value }) => <Chart data={value} />,
inlineImage: ({ value }) => (
<figure>
<img src={value.src} alt={value.alt} />
{value.caption && <figcaption>{value.caption}</figcaption>}
</figure>
),
},
}
// In your component:
<PortableText value={post.body} components={components} />To cały potok renderowania. Komponent PortableText obsługuje standardowe bloki (akapity, nagłówki, listy, znaczniki) automatycznie. Definiujesz niestandardowe komponenty tylko dla swoich niestandardowych typów.
Wielojęzyczna treść z Sanity CMS
Sanity obsługuje treści wielojęzyczne poprzez lokalizację na poziomie dokumentu (oddzielne dokumenty dla każdego języka połączone referencją kanoniczną) lub lokalizację na poziomie pola (tłumaczone pola w jednym dokumencie). Lokalizacja na poziomie dokumentu działa lepiej dla SEO i publikacji na dużą skalę – tego używamy w naszym 10-języcznym potoku.
Lokalizacja na poziomie dokumentu vs na poziomie pola
| Aspekt | Na poziomie dokumentu | Na poziomie pola |
|---|---|---|
| Podejście | Oddzielny dokument dla każdego języka | Wszystkie tłumaczenia w jednym dokumencie |
| SEO | Każdy dokument ma własny URL/slug | Pojedynczy URL, trudniej serwować strony per język |
| Złożoność zapytań | Proste filtry: language == "de" | Dostęp do zagnieżdżonych pól: title.de |
| Rozmiar treści | Małe, skupione dokumenty | Jeden duży dokument ze wszystkimi językami |
| Najlepsze dla | Posty na blogu, strony, treści napędzane SEO | Małe ciągi UI, etykiety, metadane |
| Nasza werdykt | Używamy tego do wszystkiego | Tylko dla wspólnych ciągów UI |
Wybraliśmy lokalizację na poziomie dokumentu, ponieważ każde tłumaczenie otrzymuje własny slug, własny URL i własne metadane. Turecka wersja posta o Supabase vs Firebase otrzymuje slug supabase-firebase-karsilastirma – poprawny turecki, a nie hack z parametrem URL.
Architektura naszego 10-języcznego potoku
Oto jak działa nasz zautomatyzowany potok: piszemy post po angielsku, a następnie tłumaczymy go na 9 dodatkowych języków (niemiecki, francuski, holenderski, hiszpański, turecki, włoski, szwedzki, norweski, arabski). Każde tłumaczenie przechodzi przez konwersję Markdown, generowanie Portable Text i publikację przez API Sanity.
Architektura wygląda tak:
- Pisanie, Markdown po angielsku z frontmatterem YAML
- Tłumaczenie, tłumaczenie AI na 9 języków (zweryfikowane pod kątem kompletności i diakrytyków)
- Konwersja, skrypt Pythona konwertuje każdy plik
.mddo JSON Portable Text - Publikacja, wywołania API do Sanity: utworzenie dokumentu, przesłanie obrazów, łatanie referencji
Każdy dokument ma pole language i referencję canonicalPost wskazującą na oryginał angielski. Oto zapytanie GROQ pobierające post i wszystkie jego tłumaczenia:
// Fetch a post and all its translations
*[_type == "post" && slug.current == "sanity-cms-guide" && language == "en"][0]{
title,
language,
"translations": *[
_type == "post" &&
canonicalPost._ref == ^._id
]{
title,
language,
"slug": slug.current
}
}Strona schematu jest prosta – pole language z enumem obsługiwanych języków:
defineField({
name: 'language',
title: 'Language',
type: 'string',
options: {
list: [
{ title: 'English', value: 'en' },
{ title: 'German', value: 'de' },
{ title: 'French', value: 'fr' },
{ title: 'Dutch', value: 'nl' },
{ title: 'Spanish', value: 'es' },
{ title: 'Turkish', value: 'tr' },
{ title: 'Italian', value: 'it' },
{ title: 'Swedish', value: 'sv' },
{ title: 'Norwegian', value: 'no' },
{ title: 'Arabic', value: 'ar' },
],
},
validation: (Rule) => Rule.required(),
})Jedna pułapka, której nauczyliśmy się na własnej skórze: najpierw opublikuj dokument angielski, a następnie łatuj referencje canonicalPost w tłumaczeniach, używając opublikowanego ID dokumentu, a nie prefiksu drafts.. Sanity traktuje szkice i opublikowane dokumenty jako osobne encje wewnętrznie.
Więcej szczegółów na temat tego, jak ten potok łączy się z Model Context Protocol, znajdziesz w następnej sekcji.
Funkcje AI w Sanity: MCP, Canvas i Kontekst Agenta
Sanity pozycjonuje się jako system operacyjny dla treści w erze AI. Kluczowe funkcje AI obejmują serwer MCP pozwalający agentom AI na odczyt i zapis treści, Canvas do edycji wspomaganej AI wewnątrz Studio oraz Agent Context umożliwiający produkcyjnym agentom AI odpytywanie ustrukturyzowanej treści ze świadomością schematu.
Integracja z serwerem MCP
Serwer Sanity MCP pozwala agentom AI – Claude Code, Cursor, Windsurf i innym – programowo interagować z Twoim workspace'em Sanity. Agenci mogą odczytywać schematy, wykonywać zapytania GROQ, tworzyć dokumenty i zarządzać treścią bez niestandardowych wrapperów API.
Codziennie używamy serwera Sanity MCP w naszym potoku treści. Nasi agenci AI odpytują schemat, aby zrozumieć strukturę dokumentu, pobierają istniejące posty, aby znaleźć okazje do linkowania wewnętrznego, i publikują nowe dokumenty. Protokół MCP daje agentom świadomość schematu – wiedzą, jakie pola istnieją, jakich typów oczekują i jakie reguły walidacji mają zastosowanie. Jeśli budujesz agenta AI dla biznesu workflow, jest to potężny wzorzec.
Agent Context dla produkcyjnego AI
Agent Context to osobna funkcja dla integracji AI klasy produkcyjnej. W przeciwieństwie do serwera MCP (który jest zaprojektowany dla narzędzi deweloperskich), Agent Context zapewnia dostęp tylko do odczytu, z ograniczonym zakresem, dla agentów AI, które muszą odpytywać Twoją treść w czasie rzeczywistym – pomyśl chatboty, silniki rekomendacyjne lub systemy personalizacji treści.
Różnica ma znaczenie: MCP jest dla workflow'ów build-time i redakcyjnych (narzędzia deweloperskie świadome schematu), podczas gdy Agent Context jest dla dostępu do treści w czasie runtime z odpowiednią autoryzacją i limitowaniem速率.
Ustrukturyzowana treść Sanity daje tu realną przewagę. Strona WordPress przechowuje treść jako bloki HTML – agent AI musi parsować HTML, aby zrozumieć treść. Sanity przechowuje typowane dokumenty JSON z zdefiniowanymi schematami. Agent może odpytać *[_type == "product" && category == "electronics"]{name, price, features} i otrzymać czyste, ustrukturyzowane dane. Bez scrapingu, bez parsowania, bez zgadywania.
Jak używamy Sanity w Techsy
To nie jest hipotetyczna sekcja. Prowadzimy Sanity CMS na 4 produkcyjnych stronach, publikując w 10 językach za pomocą zautomatyzowanego potoku, który budowaliśmy przez ostatni rok. Oto architektura.
Architektura naszego potoku treści
Potok prowadzi od researchu do opublikowanego posta we wszystkich 10 językach:
- Research, analiza słów kluczowych, identyfikacja luk u konkurencji, wzorce SERP
- Brief, ustrukturyzowana specyfikacja pisania z wytycznymi sekcji, liczbą słów, linkami wewnętrznymi
- Pisanie, produkcja Markdown po angielsku z frontmatterem YAML
- Konwersja, skrypt Pythona transformuje Markdown do JSON Portable Text z naszymi 5 niestandardowymi typami bloków
- Publikacja, wywołania API do Sanity:
createOrReplacedokumentu, przesyłanie obrazów do CDN Sanity, łatanie referencji autor/kategoria - Tłumaczenie, tłumaczenie AI na 9 języków, zweryfikowane pod kątem kompletności
- Publikacja tłumaczeń, ten sam przepływ konwersji/publikacji per język, z referencją
canonicalPostłataną do oryginału angielskiego
Niestandardowy schemat obsługuje typy block, table, codeBlock, chartBlock i inlineImage, wszystkie zdefiniowane jako produkcyjne obiekty schematu Sanity z regułami walidacji. Spośród narzędzi AI dla startupów, które przetestowaliśmy, ten potok oparty na Sanity był najbardziej niezawodny dla ustrukturyzowanej treści na skalę.
Lekcje z ponad 400 opublikowanych treści
Kilka rzeczy, które życzylibyśmy sobie, żeby ktoś nam wcześniej powiedział:
Kolejność łatań referencji ma znaczenie. Referencje Sanity nie mogą wskazywać na dokumenty, które jeszcze nie istnieją. Najpierw opublikuj post angielski, a następnie utwórz tłumaczenia z canonicalPost wskazującym na opublikowany ID dokumentu angielskiego. Kilka razy zepsuliśmy to na początku.
Wdrożenie schematu jest per workspace. Jeśli prowadzisz wiele projektów Sanity (my prowadzimy 4), musisz wdrażać schematy do każdego z nich osobno: npx sanity@latest schema deploy per konfiguracja projektu.
Darmowy plan jest prawdziwy. Prowadziliśmy dwie z naszych czterech stron na darmowym planie przez miesiące. 20 użytkowników, 500 tys. żądań API/miesiąc, 100 tys. żądań CDN – to wystarczy na prawdziwą stronę produkcyjną, a nie tylko projekt zabawkowy.
Konwersja Portable Text to wąskie gardło. Konwersja Markdown do Portable Text nie jest trywialna. Zagnieżdżone listy, tabele w blockquotes, bloki kodu ze specjalnymi znakami – przypadki brzegowe wszędzie. Iterowaliśmy nad naszym skryptem konwertującym przez miesiące.
Potrzebujesz pomocy w konfiguracji Sanity dla swojego projektu? Zbudowaliśmy wielojęzyczne potoki treści dla 4 stron produkcyjnych. Umów bezpłatną konsultację
Rozbicie cenowe Sanity CMS
Sanity oferuje trzy plany: Free (20 użytkowników, 500 tys. żądań API/miesiąc), Growth (15 USD/użytkownika/miesiąc z zaawansowanymi rolami i zaplanowanymi szkicami) oraz Enterprise (cena niestandardowa z SLA i funkcjami compliance). Darmowy plan jest najbardziej hojny na rynku headless CMS.
| Funkcja | Free | Growth (15 USD/użytk./mies.) | Enterprise |
|---|---|---|---|
| Użytkownicy | 20 | 50 | Nielimitowani |
| Żądania API | 500K/miesiąc | 2,5M/miesiąc | Niestandardowe |
| Żądania CDN | 100K/miesiąc | 500K/miesiąc | Niestandardowe |
| Role | Tylko Admin | Admin, Developer, Editor, Contributor | Role niestandardowe |
| Współpraca | Edycja w czasie rzeczywistym | + Zaplanowana publikacja, szkice | + Workflow'y |
| Wsparcie | Społeczność | Dedykowane + SLA | |
| Compliance | , | , | SOC 2, HIPAA |
Na darmowym planie prowadzimy dwie nasze strony bez osiągania limitów. Plan Growth za 15 USD/użytkownika/miesiąc dodał dostęp oparty na rolach (ważne, gdy mieliśmy nietechnicznych redaktorów) i zaplanowaną publikację. Widzowie są darmowi w planie Growth, co jest miłym dodatkiem – nie jesteś karany za dawanie interesariuszom dostępu do odczytu.
Jak to się ma do konkurencji?
| Funkcja | Sanity Free | Contentful Free | Strapi Cloud Free | Payload Cloud |
|---|---|---|---|---|
| Użytkownicy | 20 | 1 | 1 | 1 |
| Typy treści | Nielimitowane | 48 | Nielimitowane | Nielimitowane |
| Wywołania API | 500K/mies. | Wliczone | Wliczone | Wliczone |
| Niestandardowe typy | Tak | Limitowane | Tak | Tak |
| Cena wzrostu | 15 USD/użytk./mies. | 300 USD/mies. | 29 USD/mies. | 50 USD/mies. |
20-osobowy darmowy plan Sanity jest wyjątkowy. Contentful limituje Cię do 1 użytkownika w darmowej wersji i skacze do 300 USD/miesiąc za plan Team. Jeśli jesteś startupem lub małym zespołem, darmowy plan Sanity pozwala prowadzić rzeczywiste obciążenia produkcyjne bez wydawania ani grosza.
Sanity oferuje również program startupowy, który daje kwalifikującym się startupom rok darmowego dostępu do planu Growth. Warto aplikować, jeśli spełniasz warunki.
Często zadawane pytania
Czym jest Sanity CMS i jak działa?
Sanity CMS to platforma treści typu headless, która przechowuje ustrukturyzowane dokumenty JSON w zarządzanym backendzie zwanym Content Lake. Edytujesz treści przez Sanity Studio (konfigurowalną aplikację React), odpytujesz je za pomocą GROQ lub GraphQL i renderujesz w dowolnym frameworku frontendowym. Treści synchronizują się w czasie rzeczywistym ze wszystkimi podłączonymi klientami.
Czy Sanity CMS jest darmowe?
Tak. Darmowy plan Sanity obejmuje 20 użytkowników, 500 tys. żądań API miesięcznie i 100 tys. żądań CDN – najbardziej hojny darmowy plan wśród platform headless CMS. Plan Growth kosztuje 15 USD za użytkownika miesięcznie i dodaje dostęp oparty na rolach, zaplanowaną publikację oraz wyższe limity. Ceny Enterprise są ustalane indywidualnie.
Jaka jest różnica między Sanity a Contentful?
Sanity używa schema-as-code (schematy żyją w Twoim kodzie), GROQ do odpytywania i w pełni konfigurowalnego, open-source'owego Studio. Contentful używa modelowania treści opartego na GUI, GraphQL i hostowanego edytora z mniejszą możliwością dostosowania. Darmowy plan Sanity obejmuje 20 użytkowników w przeciwieństwie do 1 w Contentful. Contentful ma większy rynek wtyczek.
Czy Sanity CMS jest dobre dla początkujących?
Sanity Studio jest intuicyjne dla redaktorów treści – doświadczenie edycji nie wymaga wiedzy technicznej. Jednak konfiguracja schematów wymaga znajomości JavaScript lub TypeScript. Sanity zapewnia doskonałą dokumentację, szablony projektów i społeczność Slack z aktywnym wsparciem. Zacznij od npm create sanity@latest i szablonu bloga.
Czy mogę self-hostować Sanity?
Sanity Studio jest w pełni self-hostowalne, ponieważ jest to aplikacja React open-source. Możesz wdrożyć je na Vercel, Netlify lub u dowolnego dostawcy hostingu statycznego. Backend Content Lake to usługa zarządzana – nie ma opcji self-hostingu dla warstwy danych. To kompromis: otrzymujesz zerową zarządzanie infrastrukturą, ale brak kontroli nad danymi on-premise.
Jakiego rodzaju bazy danych używa Sanity?
Content Lake Sanity nie jest tradycyjną bazą SQL ani NoSQL. To zarządzany magazyn dokumentów, który przechowuje treści jako ustrukturyzowany JSON z warstwą zapytań GROQ na górze. Nie interagujesz bezpośrednio z bazą danych – robisz to przez API Sanity. Dokumenty mają wbudowaną pełną historię wersji i synchronizację w czasie rzeczywistym.
Czy Sanity CMS jest open source?
Sanity Studio jest open source na licencji MIT – możesz je forknąć, dostosować i self-hostować. Backend Content Lake to proprietarny SaaS. Specyfikacja języka zapytań GROQ jest również open source, opublikowana na GitHubie. Specyfikacja Portable Text również jest open source, utrzymywana na portabletext.org.
Czym jest Portable Text w Sanity?
Portable Text to specyfikacja Sanity dla ustrukturyzowanego bogatego tekstu. Zamiast przechowywać treść jako ciągi HTML, reprezentuje akapity, nagłówki, obrazy i niestandardowe bloki jako typowane obiekty JSON w tablicy. Dzięki temu treść jest przenośna między frameworkami i platformami. Możesz definiować niestandardowe typy bloków, takie jak fragmenty kodu, wykresy i tabele, z własnymi ustrukturyzowanymi polami.
Czym jest GROQ i чем różni się od GraphQL?
GROQ (Graph-Relational Object Queries) to natywny język zapytań Sanity. Jego składnia, *[filter]{projection}, jest bardziej zwięzła niż GraphQL dla danych Sanity, z wbudowanym wsparciem dla joinów przez operator -> i pól obliczeniowych. GraphQL jest również dostępny dla zespołów, które preferują standaryzowane narzędzia lub już używają Apollo Client.
Jak Sanity obsługuje treści wielojęzyczne?
Sanity obsługuje lokalizację na poziomie dokumentu (oddzielne dokumenty per język połączone referencjami kanonicznymi) i lokalizację na poziomie pola (tłumaczone pola w jednym dokumencie). Lokalizacja na poziomie dokumentu jest lepsza dla SEO, ponieważ każde tłumaczenie otrzymuje własny URL i metadane. Używamy lokalizacji na poziomie dokumentu do publikacji w 10 językach z zautomatyzowanymi potokami tłumaczenia i publikacji.