Astro i Sanity CMS — dlaczego warto połączyć te technologie
Kombinacja tych dwóch narzędzi jest mocna, ponieważ każde z nich robi coś innego i ich role się nie nakładają. Sanity dostarcza ustrukturyzowaną treść, a Astro zamienia ją w czysty HTML bez zbędnego JavaScriptu.
Sekret tkwi w (Astro Islands). Klasyczny framework typu wysyła do przeglądarki cały JavaScript potrzebny do zbudowania strony, nawet jeśli to zwykły artykuł, który nic nie robi po załadowaniu. Astro odwraca tę logikę: domyślnie renderuje wszystko do statycznego HTML-a i nie wysyła żadnego JavaScriptu. Interaktywność dokładasz tylko punktowo, używając do tego celu wyspy (np. menu mobilne czy formularz), które się , podczas gdy reszta strony pozostaje czystym, lekkim HTML-em.
Dla strony opartej o treść to idealne dopasowanie, ponieważ treść z Sanity i tak nie potrzebuje interaktywności — to po prostu tekst, obrazy i nagłówki. Astro pobiera te dane raz, podczas builda, a potem zmienia je w gotowe pliki HTML. W efekcie strona ładuje się błyskawicznie, a Lighthouse pokazuje wyniki w okolicach 100/100 praktycznie od razu. Dzieje się tak ponieważ nie ma czego ładować poza HTML-em i CSS-em.
Jak połączyć Astro z Sanity CMS krok po kroku
Sanity utrzymuje oficjalną integrację @sanity/astro, więc nie wymyślasz koła na nowo. Instalacja to jedna komenda:
@astrojs/react jest potrzebny, jeśli chcesz korzystać z wizualnej edycji albo osadzić Sanity Studio na trasie w projekcie Astro. Warto przy okazji dograć pakiety pomocnicze:
astro-portabletext— renderuje Portable Text (format tekstu sformatowanego z Sanity) do HTML-a,@sanity/image-url— buduje URL-e obrazów z transformacjami z -u Sanity,groq— eksportujedefineQuerydo typowanych zapytań.
Konfigurację dodajesz w astro.config.mjs:
W produkcji projectId, dataset i tokeny trzymaj w zmiennych środowiskowych. Sam projectId nie jest sekretem, ale jeden spójny sposób konfiguracji ogranicza ryzyko pomyłek między środowiskiem testowym i produkcyjnym.
Integracja wystawia gotowego klienta Sanity jako wirtualny moduł sanity:client, którego importujesz w dowolnym komponencie i nie musisz ręcznie konfigurować połączenia w każdym pliku. Dla TypeScript warto dodać też deklarację typów:
Gdzie mieszka Sanity Studio i schemat treści
Zanim przejdziemy dalej, muszę zwrócić uwagę na jedną sprawę: Sanity Studio, czyli panel, w którym redaktor tworzy treść, to osobna aplikacja i trzeba zdecydować, gdzie będzie funkcjnować. Istnieją dwie drogi.
Klasycznym rozwiązaniem jest osobny projekt Studio. Uruchamiasz npm create sanity@latest, kreator zakłada projekt w Sanity (dostajesz projectId) i generuje aplikację Studio z folderem na schematy. Wtedy plik sanity/schemas/post.js z następnej sekcji trafia właśnie tam, a Studio wystawiasz osobno (np. npx sanity deploy daje darmowy hosting pod *.sanity.studio).
Studio osadzone w projekcie Astro. Bardzo wygodne, gdy chcesz mieć wszystko w jednym repozytorium. Integracja @sanity/astro montuje Studio na wskazanej trasie przez opcję studioBasePath (to do tego potrzebny był @astrojs/react):
Przy tym wariancie w korzeniu projektu Astro tworzysz jeszcze sanity.config.ts, który definiuje Studio i rejestruje schematy:
Dla małego bloga wariant osadzony jest najprostszy, czyli będziemy mieli w ten sposób panel pod /admin, schematy w tym samym repo co frontend. Osobne Studio wybierz, gdy nad treścią pracuje większy zespół albo z jednego Sanity korzysta kilka frontendów. Tylko wtedy ma to sens.
Minimalny model wpisu w Sanity
Zanim Astro zacznie pobierać dane, Sanity musi wiedzieć, jak wygląda wpis blogowy. Minimalny model powinien zawierać tytuł, slug, opis, datę publikacji, obraz główny i treść w Portable Text:
To jest dość prosty schemat, ale w zupełności wystarczający dla bloga. W większym projekcie dołożysz autora, kategorie, tagi, kanoniczny URL, status publikacji, powiązane wpisy albo pola pod Open Graph. Zrób to najlepiej od razu, by nie rozgrzebywać tego tematu dwa razy — szkoda czasu.
Jak pobierać dane z Sanity CMS w Astro za pomocą GROQ
Tu wkracza , czyli język zapytań Sanity. Jest trochę jak dla grafu dokumentów JSON: filtrujesz, sortujesz, robisz projekcje i joiny w jednym zapytaniu. Sanity wystawia też , jeśli masz takie preferencje, ale GROQ jest natywny i zwykle zwięźlejszy. Wybór należy do Ciebie.
W Astro pobierasz dane bezpośrednio we komponentu, czyli w bloku między ---, który wykonuje się na serwerze podczas builda i nigdy nie trafia do przeglądarki:
To zapytanie GROQ czyta się tak: weź wszystkie dokumenty typu post, które mają sluga, posortuj od najnowszego i zwróć tylko te cztery pola. Cała ta logika wykonuje się raz, podczas builda, a potem użytkownik dostaje gotowy HTML.
Strona pojedynczego wpisu z getStaticPaths
Lista wpisów to dopiero połowa bloga, podczas gdy druga połowa to dynamiczna trasa src/pages/blog/[slug].astro, która podczas builda generuje osobną stronę dla każdego sluga z Sanity:
To jest moment, w którym widać jak dobry jest Astro. getStaticPaths() pobiera wszystkie wpisy jednym zapytaniem GROQ i przekazuje każdy z nich do właściwej strony przez props — dzięki temu build nie odpytuje Sanity osobno dla każdego sluga (klasyczny problem N+1), co przy setkach artykułów wyraźnie skraca czas budowania. Każda strona wpisu powstaje jako gotowy HTML: użytkownik nie czeka na Sanity, bo w trybie statycznym CDN serwuje gotowe pliki i nie odpytuje CMS-a przy każdej wizycie, a crawler dostaje treść od razu w odpowiedzi.
Portable Text i obrazy z Sanity
Treść z Sanity nie jest Markdownem ani HTML-em, ponieważ domyślny edytor zapisuje ją jako Portable Text, czyli strukturalny JSON. Dzięki temu treść jest przenośna, ale musisz ją wyrenderować po stronie Astro.
Najprostszy wariant to komponent PortableText:
Do obrazów użyj @sanity/image-url, żeby korzystać z transformacji Sanity CDN i nie wstawiać surowych assetów bez kontroli rozmiaru:
Wtedy w komponencie możesz wygenerować obraz dopasowany do layoutu:
To drobiazg, który ma duże znaczenie dla Core Web Vitals. Jeśli wrzucisz oryginalny obraz 4000 px z biblioteki mediów, Astro nadal będzie szybkie, ale LCP może ucierpieć przez zbyt ciężki asset.
Skoro gramy pod wyniki w Google, domknij też warstwę SEO: dołóż oficjalną integrację @astrojs/sitemap (jedna pozycja w astro.config.mjs generująca sitemap.xml przy buildzie), a link kanoniczny i tagi Open Graph generuj w layoutcie z pól wpisu — obraz OG masz już gotowy dzięki transformacjom urlFor w rozmiarze 1200×630.
SSG czy SSR w Astro z Sanity CMS — który tryb wybrać?
Wybór sposobu renderowania wpływa na to, kiedy treść z Sanity trafia na stronę:
(domyślny) — każde zapytanie GROQ wykonuje się raz, podczas builda, a strony serwowane są jako statyczne pliki. Idealny, gdy treść nie zmienia się co minutę i możesz pozwolić sobie na chwilę zwłoki między publikacją a przebudową (zwykle wyzwalaną z Sanity). To wybór dla większości blogów i stron firmowych, ponieważ jest najszybszy i najtańszy.
Renderowanie na żądanie () — strony renderują się przy każdym żądaniu, więc zapytania GROQ działają w czasie rzeczywistym i zmiany widać natychmiast, bez przebudowy. Potrzebujesz do tego adaptera pod swój hosting (np. Vercel). Sięgasz po niego, gdy potrzebujesz podglądu draftów, wizualnej edycji albo treści personalizowanej.
W praktyce najczęściej wybierzesz model mieszany — i w nowszych wersjach Astro nie wymaga on przełączania całego projektu na tryb serwerowy. Zostajesz przy domyślnym buildzie statycznym, a trasom, które mają renderować się na żądanie (podgląd draftów, Visual Editing), wyłączasz prerendering wpisem export const prerender = false we frontmatterze (plus adapter). Publiczny blog pozostaje statyczny i nie płacisz wydajnością całej strony tylko dlatego, że redakcja potrzebuje wygodnego podglądu.
Przy okazji domknij regułę useCdn z konfiguracji: dla zapytań wykonywanych w buildzie zostaw false (najświeższe dane prosto z API), ale jeśli masz trasy SSR odpytujące opublikowaną treść w runtime, tam ustaw true — odpowiedzi lecą wtedy z CDN-u Sanity, szybciej i taniej niż bicie prosto w API przy każdym żądaniu.
Webhook po publikacji w Sanity
Statyczna strona nie zaktualizuje się sama po kliknięciu „Publish" w Sanity. Potrzebuje sygnału, który uruchomi nowy build i najprostszym jest wariant webhook z Sanity do platformy hostingowej:
- W panelu Sanity wejdź w projekt i sekcję API / Webhooks.
- Utwórz webhook dla dokumentów typu
post. - Jako adres podaj build hook z Vercela, Netlify albo Cloudflare Pages.
- Ustaw trigger na publikację, aktualizację i usunięcie wpisu.
- Dodaj sekret albo podpis żądania, jeśli platforma na to pozwala.
Ten mechanizm domyka cały przepływ, ponieważ redaktor publikuje wpis w Sanity, narzędzie wysyła webhook, platforma uruchamia build Astro, a CDN dostaje świeży HTML. Przy małym i średnim blogu to zwykle prostsze, tańsze i stabilniejsze niż renderowanie każdej strony na żądanie.
Ograniczenia integracji Astro z Sanity CMS
@sanity/astro jest lżejsze niż next-sanity i celowo nie ma kilku jego funkcji.
Nie działa to jak prosty przełącznik „włącz live preview". next-sanity ma bardzo głęboką integrację z Next.js, Live Content API i rewalidacją cache. W Astro standardowy, najprostszy model to statyczny build plus webhook. Visual Editing jest możliwy, ale wymaga osobnego setupu: tokenu do draftów, trybu podglądu, Presentation Tool, stega encoding i tras działających po stronie serwera.
Dla bloga czy strony firmowej nie jest to większy problem, bo publikacja z kilkudziesięciosekundowym opóźnieniem po buildzie jest akceptowalna. Problem pojawia się, gdy budujesz aplikację, w której redakcja musi widzieć zmiany natychmiast, a frontend ma dużo logiki. Wtedy warto rozważyć Next.js z Payload albo Sanity z Next.js.
