Jetzt mit KI-gestütztem Page Building über den MCP-Server
Rendering

cmssy-Inhalte in deiner App rendern

Eine Catch-all-Route macht aus jeder veröffentlichten Seite - Blöcke, Layout, SEO und Sprache - server-gerendertes HTML in deinem eigenen Next.js-Frontend.

Rendering

Die Catch-all-Route, Seiten-Komponente, Metadaten, Layout, lokalisiertes Routing und ISR-Revalidierung.

Last updated: 24. Juli 2026

Die Catch-all-Route

Ein cmssy-Workspace speichert Inhalte; deine App rendert sie. Eine optionale Catch-all-Route - app/[[...path]]/page.tsx - bildet jeden veröffentlichten Slug (und sein Sprach-Präfix) auf eine Seite ab. createCmssyPage lädt die Seite für den aktuellen Pfad, ordnet jeden Block deiner registrierten Komponente zu und rendert ihn auf dem Server.

// app/[[...path]]/page.tsx
import { buildCmssyMetadata, createCmssyPage } from "@cmssy/next/server";
import { cmssy } from "@/cmssy/config";
import { blocks } from "@/cmssy/blocks";

export const revalidate = 3600;
export const dynamicParams = true;

const renderPage = createCmssyPage(cmssy, blocks);

type PageProps = { params: Promise<{ path?: string[] }> };

export async function generateMetadata({ params }: PageProps) {
  return buildCmssyMetadata(cmssy, (await params).path);
}

export default function Page({ params }: PageProps) {
  return renderPage({ params });
}

Die Catch-all-Route ist statisch renderbar: Sie liest nie searchParams oder headers(), sodass ISR aktiv bleibt. Die Entwurfsvorschau funktioniert weiterhin pro Request über draftMode().


Seiten-Komponente & Konfiguration

createCmssyPage(config, blocks, options?) gibt eine asynchrone Server-Komponente zurück. config stammt aus defineCmssyConfig; blocks ist das Array der defineBlock-Definitionen, die das SDK über type zuordnet. Übergib { editor }, um den visuellen Editor auf der Vorschau-Route einzuhängen, oder { path }, um eine Einzelseiten-Route festzulegen.

// cmssy/config.ts
import { defineCmssyConfig } from "@cmssy/next";

export const cmssy = defineCmssyConfig({
  apiUrl: process.env.CMSSY_API_URL,
  org: process.env.CMSSY_ORG_SLUG,
  workspaceSlug: process.env.CMSSY_WORKSPACE_SLUG,
  draftSecret: process.env.CMSSY_DRAFT_SECRET,
});

Metadaten & SEO

buildCmssyMetadata(config, path, options?) gibt ein vollständiges Next.js-Metadata-Objekt aus den SEO-Feldern der Seite und dem Workspace-Branding zurück: Titel, Beschreibung, Keywords, Canonical sowie hreflang-Alternativen pro Sprache und Open-Graph-/Twitter-Karten. Rufe es in generateMetadata auf (siehe Route oben).

Übergib den Catch-all-Pfad wie geroutet - inklusive Sprach-Präfix. Das Präfix sagt dem SDK, welche Sprache zu rendern ist; entfernst du es zuerst, erbt jede Übersetzung den Titel der Standardsprache und ein Canonical, das auf die Standard-URL zeigt - für Suchmaschinen ist die Übersetzung damit ein Duplikat.

buildCmssyMetadata(cmssy, path, {
  image: "https://assets.cmssy.io/og.png",
  ogType: "article",
});

Header & Footer

Gemeinsame Bereiche - Header, Footer - sind Layout-Blöcke. Rendere sie in app/[[...path]]/layout.tsx: fetchLayouts lädt die Layout-Gruppen, CmssyServerLayout rendert eine position, und CmssyLocaleProvider stellt die aktive Sprache den Client-Blöcken bereit.

// app/[[...path]]/layout.tsx
import { fetchLayouts, resolveSiteLocales, CmssyServerLayout } from "@cmssy/react";
import { splitCmssyLocale } from "@cmssy/core";
import { CmssyLocaleProvider } from "@cmssy/next/client";
import { blocks } from "@/cmssy/blocks";
import { cmssy } from "@/cmssy/config";

export default async function Layout({
  children,
  params,
}: {
  children: React.ReactNode;
  params: Promise<{ path?: string[] }>;
}) {
  const { path } = await params;
  const [{ locale }, siteLocales, groups] = await Promise.all([
    splitCmssyLocale(cmssy, path),
    resolveSiteLocales(cmssy),
    fetchLayouts(cmssy, "/"),
  ]);

  const slot = (position: "header" | "footer") => (
    <CmssyServerLayout
      groups={groups}
      blocks={blocks}
      position={position}
      locale={locale}
      defaultLocale={siteLocales.defaultLocale}
      enabledLocales={siteLocales.locales}
    />
  );

  return (
    <html lang={locale}>
      <body>
        <CmssyLocaleProvider
          value={{
            current: locale,
            default: siteLocales.defaultLocale,
            enabled: siteLocales.locales,
          }}
        >
          {slot("header")}
          {children}
          {slot("footer")}
        </CmssyLocaleProvider>
      </body>
    </html>
  );
}

Lokalisiertes Routing

cmssy kodiert die Sprache als Pfad-Präfix: /about ist die Standardsprache, /pl/about ist Polnisch. Dieselbe Catch-all-Route bedient beide. splitCmssyLocale(config, path) liest die aktivierten Sprachen des Workspace und trennt das Präfix vom Rest - ohne Request oder Header, also statisch-sicher.

const { locale, path: rest } = await splitCmssyLocale(cmssy, path);
// ["pl", "about"]  ->  locale "pl", rest ["about"]
// ["about"]        ->  locale "en" (default), rest ["about"]

ISR & On-Demand-Revalidierung

Veröffentlichte Seiten werden gecacht und per ISR über export const revalidate neu generiert. Für sofortige Aktualisierung beim Publizieren füge eine On-Demand-Route hinzu und richte den content.changed-Webhook des Workspace darauf. cmssy sendet den geänderten path per POST; die Route revalidiert ihn oder die gesamte Site (layout-Scope), wenn kein Pfad gesendet wird. Sichere sie mit einem gemeinsamen Secret.

// app/api/revalidate/route.ts
import { revalidatePath } from "next/cache";
import { NextResponse, type NextRequest } from "next/server";

export async function POST(request: NextRequest) {
  const secret = process.env.CMSSY_REVALIDATE_SECRET;
  const provided =
    request.nextUrl.searchParams.get("secret") ??
    request.headers.get("x-revalidate-secret");
  if (!secret || provided !== secret) {
    return NextResponse.json({ revalidated: false }, { status: 401 });
  }

  const { path } = (await request.json().catch(() => ({}))) as { path?: string };
  if (path) revalidatePath(path);
  else revalidatePath("/", "layout");

  return NextResponse.json({ revalidated: true, path: path ?? "/" });
}

Nächste Schritte

Rendering - Cmssy Headless SDK