Czego potrzebujesz na start

Node 20+, menedżer pakietów (przykłady używają pnpm, npm działa tak samo), workspace w cmssy i jeden token API. Token tworzysz na karcie setupu na stronie głównej workspace'u albo w Settings → API Tokens; zaczyna się od cs_. Czterdzieści minut od początku do końca, większość na czytanie. Deploy (krok 7) wymaga dowolnego hostingu z Next.js; przejście zweryfikowano z tunelem w roli deployu.

Twój pierwszy blok na żywo

Jedna ścieżka, bez skrótów: świeża aplikacja Next.js, okablowanie cmssy, Twoja lokalna strona osadzona w edytorze, własny blok edytowany w miejscu, potem deploy, promocja, publikacja - i webhook, dzięki któremu publikacja pokazuje się od razu, a nie po wygaśnięciu cache'u. Każdy krok poniżej został przejechany od początku do końca 2026-09-20 z @cmssy/cli 16.11.0 i Next.js 16.3.

20 września 2026

Pozostałe strony tej sekcji tłumaczą każdy element z osobna. Ta przechodzi całą ścieżkę po kolei, z dokładnymi komendami i dokładnymi etykietami, które zobaczysz. Na końcu masz stronę w Next.js renderującą Twój workspace, własny blok na opublikowanej stronie i workspace, który mówi Twojej stronie, kiedy się odświeżyć.

1. Utwórz aplikację i okabluj cmssy

Zacznij od generatora swojego frameworka, a potem pozwól CLI cmssy dodać okablowanie. Next.js wykrywa z package.json. CLI wołaj zawsze jako @cmssy/cli@latest: inaczej stara globalna instalacja wygra i wywali się z Cannot query field "myWorkspaces".

npx create-next-app@latest my-site   # App Router: yes
cd my-site
npx @cmssy/cli@latest init
pnpm install

init zapisuje 16 plików i nigdy nie nadpisuje bez --force:

  • cmssy.config.ts - czyta CMSSY_ORG_SLUG, CMSSY_WORKSPACE_SLUG i CMSSY_DRAFT_SECRET prosto z process.env, więc brakująca zmienna wywala się przy starcie z nazwą, a nie gdzieś dalej bez związku. .env.example je wylicza; prawdziwy .env.local wypełni link w następnym kroku.
  • proxy.ts - preset middleware: prefiksy języków, zweryfikowane przepisanie do trybu edycji i CSP, które pozwala osadzać stronę tylko panelowi cmssy.
  • cmssy/blocks.ts - rejestr bloków, z przykładowym blokiem hero w blocks/hero/. Obok: cmssy/editor.tsx (ładuje rejestr leniwie po stronie klienta), cmssy/editable-layout.tsx (nagłówek i stopka przez mostek edycji) i cmssy/site-providers.tsx (jedyne miejsce na Twoje providery).
  • app/[[...path]]/page.tsx i layout.tsx - publiczna trasa catch-all renderująca każdą stronę cmssy, plus services/pages.ts, z którego czyta. app/page.tsx i app/layout.tsx z generatora lądują w .cmssy-backup/.
  • app/cmssy-edit/[[...path]]/ - trasa, na którą przepisywany jest edytor, z własnym root layoutem; oba layouty importują globals.css, więc trzymaj je w zgodzie, gdy dodajesz CSS albo metadane.
  • app/api/draft/route.ts i app/api/revalidate/route.ts - podgląd roboczy i webhook publikacji, który podłączysz w kroku 8.

init wpina też @cmssy/eslint-plugin do eslint.config.mjs i dodaje @cmssy/next, @cmssy/react, @cmssy/core oraz plugin do package.json w swojej wersji - dlatego instalacja jest po nim.

2. Podłącz aplikację do workspace'u

npx @cmssy/cli@latest link --token cs_...

link pobiera tokenem slug organizacji, slug workspace'u i sekret draftu, zapisuje całą trójkę do .env.local, sprawdza, że workspace odpowiada, sekret się zgadza i /api/draft jest zamontowane, a na końcu drukuje link do edytora tego workspace'u. Jeśli workspace nie ma jeszcze manifestu bloków, wypycha go z Twojego rejestru, żeby paleta edytora znała Twoje bloki przed pierwszym deployem. Gdy token widzi kilka workspace'ów, zapyta, o który chodzi - w powłoce nieinteraktywnej zamiast tego się zatrzyma, więc podaj --workspace <slug>. Na końcu wypisuje linki podglądu roboczego z wbudowanym sekretem: nie wklejaj ich nigdzie publicznie.

Wybieraj link zamiast przepisywania ręcznego. Jeśli jednak przepisujesz, wszystko jest w Settings → Headless: Organization, Workspace i Draft preview secret z przyciskiem Copy. Regenerate natychmiast unieważnia stary sekret - zaktualizuj wtedy .env.local.

3. Środowisko w jednym miejscu

ZmiennaUstawiaDo czego
CMSSY_ORG_SLUGlinkSlug organizacji. Wymagana.
CMSSY_WORKSPACE_SLUGlinkSlug workspace'u. Wymagana.
CMSSY_DRAFT_SECRETlinkTylko po stronie serwera. Pilnuje podglądu roboczego i trybu edycji: edytor go wysyła, proxy.ts weryfikuje. Wymagana.
CMSSY_WEBHOOK_SECRETTy, krok 8Sekret podpisu webhooka content.changed, który trafia w /api/revalidate. Bez niego trasa odpowiada 500.
CMSSY_API_TOKENopcjonalnaPozwala pominąć --token w komendach CLI. Działająca strona nigdy jej nie potrzebuje.
NEXT_PUBLIC_SITE_URLopcjonalnaTwój publiczny adres, jeśli budujesz canonical, hreflang albo sitemapę jak starter. cmssy przechowuje treść kanoniczną, nigdy Twoją domenę.

Te same nazwy ustaw w środowisku hostingu przy deployu (krok 7). Nie dodawaj fallbacków ?? "": pusty slug to ciche 404, nazwana brakująca zmienna to poprawka.

4. Uruchom i osadź w edytorze

pnpm dev   # http://localhost:3000

Sam localhost:3000 renderuje treść opublikowaną - świeży workspace pokazuje stronę startera Nothing published yet at / i tak ma być. Edycja na żywo dzieje się w edytorze cmssy, który osadza Twoją działającą aplikację:

  1. Otwórz link do edytora, który wydrukował link (albo Pages w panelu) i otwórz stronę - utwórz ją, jeśli workspace jest pusty.
  2. W nagłówku edytora kliknij Dev host. Włącz Enable dev host, wpisz http://localhost:3000 w polu Local dev host i kliknij Apply. Jeśli strona ma niezapisane zmiany, potwierdzisz Enter dev preview?.

Pojawia się niebieski baner: Dev preview - changes save to your isolated dev buffer, not production. Canvas pokazuje teraz Twoją lokalną aplikację, a paleta bloków (Add block albo zakładka Blocks w lewym panelu) listuje to, co eksportuje cmssy/blocks.ts - na razie sam Hero. Edytor czyta schematy z osadzonej strony przy każdym handshake'u. Trzy rzeczy o Dev hoście:

  • Przyjmuje tylko adresy localhost (localhost, 127.0.0.1, ::1, *.localhost), http lub https. Chrome osadza zwykłe http z edytora na https; Firefox i Safari mogą to blokować - tam użyj tunelu.
  • Jest tylko Twój. Adres żyje w sesji przeglądarki, a włącznik jest per użytkownik. Zespół dalej używa zapisanego Preview URL (Settings → Headless), którego Dev host nie rusza.
  • Dopóki jest włączony, zmiany idą do Twojego bufora dev, nie do wspólnego draftu strony, a Publish jest wyłączony. Krok 7 przenosi bufor do prawdziwego draftu. Przeładowanie edytora nie szkodzi: osadzona strona ładuje wspólny draft, a edytor odkłada bloki z Twojego bufora z powrotem na canvas, gdy tylko strona się zgłosi.

5. Napisz blok

Niech CLI go wygeneruje; nazwa musi być w kebab-case:

npx @cmssy/cli@latest add block feature-card

Powstają dwa pliki, a blok zostaje zarejestrowany w cmssy/blocks.ts za Ciebie:

// blocks/feature-card/FeatureCard.tsx
import { fields, type BlockProps } from "@cmssy/react";

export const featureCardProps = {
  heading: fields.text({ label: "Heading", required: true }),
  text: fields.textarea({ label: "Text" }),
};

export default function FeatureCard({
  content,
}: BlockProps<typeof featureCardProps>) {
  return (
    <section>
      <h2>{content.heading}</h2>
      {content.text ? <p>{content.text}</p> : null}
    </section>
  );
}
// blocks/feature-card/block.ts
import { defineBlock } from "@cmssy/react";
import FeatureCard, { featureCardProps } from "./FeatureCard";

export const featureCardBlock = defineBlock({
  type: "feature-card",
  label: "Feature card",
  component: FeatureCard,
  props: featureCardProps,
});

Schemat jest jedynym miejscem, w którym pole ma nazwę: BlockProps<typeof featureCardProps> typuje z niego content, więc zmiana nazwy heading to błąd kompilacji, a nie pusta karta. Nadaj scaffoldowi jakiś kształt, zanim wrócisz do edytora - przychodzi bez stylów, a pusty <h2> ma zero pikseli wysokości, więc do momentu wpisania tekstu nic nie zobaczysz:

<section className="mx-auto max-w-2xl px-6 py-16">
  <h2 className="text-3xl font-semibold">{content.heading}</h2>
  {content.text ? <p className="mt-4 text-lg">{content.text}</p> : null}
</section>

Restart nie jest potrzebny: hot reload przeładowuje osadzoną stronę, a edytor czyta schematy na nowo - Feature Card pojawia się w palecie w kilka sekund. Jeśli nie, przeładuj raz stronę edytora. Reszta o polach, loaderach i blokach layoutu jest w Przewodniku tworzenia bloków.

6. Wrzuć go na stronę i edytuj na żywo

Z powrotem w edytorze (Dev host wciąż włączony): przeciągnij Feature Card z palety na canvas, wypełnij Heading i Text w panelu właściwości i patrz, jak sekcja w Twojej aplikacji aktualizuje się w trakcie pisania. Zmień markup komponentu w swoim edytorze kodu, a canvas pójdzie za tym przy hot reloadzie.

Dla nowego bloku nie pojawia się żadne ostrzeżenie: edytor traktuje typ zadeklarowany przez osadzoną stronę jako znany, choć manifest bloków workspace'u pozna go dopiero w kroku 7. Jeśli żółte ostrzeżenie jednak wymienia jakiś typ, działająca strona naprawdę go nie renderuje - przywróć blok w kodzie albo usuń go ze strony.

Kliknij Save page. Toast mówi Saved to your dev buffer: to Twoja prywatna kopia robocza, więc blok, którego nie ma jeszcze na wdrożonej stronie, nie zepsuje nikomu podglądu.

7. Wdróż blok, promuj, opublikuj

Trzy rzeczy muszą znać feature-card, zanim jego treść pójdzie publicznie: wdrożona strona (żeby odwiedzający ją wyrenderowali), wspólny podgląd edytora (który osadza zapisany Preview URL) i manifest bloków workspace'u (względem którego Promote to draft waliduje). W tej kolejności:

  1. Wdróż aplikację (np. vercel albo push do podłączonego repozytorium) z tymi samymi zmiennymi z kroku 3 ustawionymi w środowisku hostingu.
  2. Skieruj Preview URL na deploy - Settings → Headless → Preview URL, albo npx @cmssy/cli@latest link --token cs_... --preview-url https://my-site.vercel.app.
  3. Zaktualizuj manifest bloków z kodu, który właśnie wdrożyłeś: npx @cmssy/cli@latest sync-manifest --token cs_.... Wypisuje dokładnie, co się zmienia (tu: adds feature-card) i aktywuje nowy manifest. Alternatywa bez CLI: wyłącz raz Dev host - edytor czyta bloki wdrożonej strony i, bo ta zmiana tylko dodaje typ, aktywuje ją sam; zmiana, która usuwa albo przebudowuje typy, jest tylko proponowana i czeka na przegląd w Settings → Headless → Block manifest.
  4. Z powrotem w edytorze z włączonym Dev hostem kliknij Promote to draft w niebieskim banerze. Twój bufor dev staje się prawdziwym draftem strony. Pominięcie punktu 3 kończy się toastem z nazwą brakującego typu - Typ bloku feature-card nie jest jeszcze w manifeście bloków workspace'u - i tymi samymi dwiema drogami wyjścia.
  5. Wyłącz Dev host (Dev host → przełącznik; potwierdź Exit dev preview?, jeśli zapyta). Canvas osadza teraz wdrożoną stronę i renderuje tam Twój blok.
  6. Kliknij Publish.

Publikacja niczego nie wdraża - blok poszedł na produkcję w punkcie 1; publikacja tylko przełącza treść na publiczną.

8. Żeby publikacja pokazała się od razu: webhook rewalidacji

Trasa catch-all wygenerowana przez init cache'uje: export const revalidate = 3600. Bez pomocy opublikowana strona serwuje starą kopię nawet przez godzinę. cmssy zamyka tę lukę, wołając Twoją stronę przy każdej publikacji - Ty tylko rejestrujesz endpoint.

  1. W panelu otwórz Settings → Webhooks i kliknij Add endpoint.
  2. URL: https://twoja-strona.com/api/revalidate. Zdarzenia: content.changed (jedno zdarzenie odpalane przy każdej zmianie strony, rekordu, mediów i ustawień; do cache'u nie potrzebujesz tych szczegółowych).
  3. Skopiuj sekret od razu - pokazuje się tylko raz. Ustaw go jako CMSSY_WEBHOOK_SECRET w środowisku hostingu i zrób redeploy.

Co dzieje się przy publikacji: cmssy podpisuje payload HMAC-SHA256 z timestamp.body i wysyła go z nagłówkiem x-cmssy-signature: t=…,v1=…. createCmssyRevalidateRoute w app/api/revalidate/route.ts weryfikuje podpis Twoim sekretem i unieważnia tagi cache'u treści cmssy, więc następne żądanie renderuje świeżą treść. Recent deliveries na tej samej stronie ustawień pokazuje każde wywołanie i odpowiedź; Rotate secret jest tam, gdy potrzebujesz nowego - stary przestaje działać natychmiast, więc aktualizuj środowisko w tym samym ruchu.

Żeby sprawdzić to przed deployem, wystaw serwer dev tunelem (cloudflared tunnel --url http://localhost:3000 albo ngrok), zarejestruj adres tunelu jako endpoint, wpisz sekret do .env.local i zrestartuj pnpm dev (zmiany env nie są hot-reloadowane). Publikacja pokaże się wtedy jako POST /api/revalidate 200 w terminalu i jako wiersz success w Recent deliveries.

9. Jeśli coś się nie zgadza

  • Bloku nie ma w palecie. Czy Dev host jest włączony i wskazuje port, na którym działa aplikacja? Czy blok jest w tablicy blocks w cmssy/blocks.ts? Przeładuj raz stronę edytora.
  • Blok jest na liście warstw, ale canvas nic nie pokazuje. Blok jest pusty albo bez paddingu (pusty nagłówek nie ma wysokości) albo schowany pod żółtym ostrzeżeniem - wpisz nagłówek, zamknij ostrzeżenie, dodaj padding.
  • Canvas pokazuje treść opublikowaną zamiast trybu edycji. Sekret draftu wysłany przez edytor nie zgadza się z Twoim CMSSY_DRAFT_SECRET - uruchom link jeszcze raz. Niezweryfikowane żądanie edycji celowo renderuje publiczną stronę.
  • Promote to draft mówi, że typu bloku nie ma jeszcze w manifeście bloków workspace'u. Manifest jest starszy niż Twój blok. Uruchom sync-manifest z wdrożonego kodu albo wyłącz raz Dev host, gdy Preview URL wskazuje deploy z tym blokiem, i promuj ponownie.
  • Publish jest wyszarzony. Jesteś w dev preview. Promote to draft, wyjdź z Dev hosta, publikuj.
  • Opublikowane, ale strona pokazuje starą wersję. Sprawdź Recent deliveries w Settings → Webhooks. 500 znaczy, że na hostingu brakuje CMSSY_WEBHOOK_SECRET; 401 - że się nie zgadza; brak jakiejkolwiek dostawy - że endpoint nie subskrybuje content.changed.
  • link mówi, że workspace ma już manifest. Normalne po pierwszym uruchomieniu. sync-manifest podmienia go, gdy chcesz, żeby paleta odpowiadała zmienionemu rejestrowi bez otwierania edytora.
  • npx @cmssy/cli pada z „Cannot query field myWorkspaces”. Odpowiedziało stare globalne @cmssy/cli zamiast aktualnego. Użyj npx @cmssy/cli@latest albo usuń globalną instalację.

Dalej

  • Schema i typy pól - każde pole, repeatery, pola warunkowe.
  • Loadery serwerowe - pobieranie podczas SSR bez wysyłania zapytania do klienta.
  • Podgląd roboczy - trzy sposoby oglądania nieopublikowanej treści i czemu samo ?cmssyEdit=1 nic nie robi.
  • Webhooki - wszystkie zdarzenia i podpis w szczegółach.
  • CLI - init, link, add block, sync-manifest.

Buduj dalej

Pola, loadery i bloki layoutu zaczynają się tam, gdzie kończy się pierwszy blok.