Désormais avec création de pages par IA via le serveur MCP
Documentation

Fonctionnalités avancées des blocs

Blocs de layout, style Tailwind, composants serveur vs client et récupération de données avec les loaders serveur.

Last updated: 29 juin 2026

Blocs de layout

Le header, le footer et les autres régions partagées sont des blocs de layout — identiques aux blocs de page, mais marqués avec layoutPositions et rendus par CmssyServerLayout par position.

// blocks/header/block.ts
import type { ComponentType } from "react";
import { defineBlock, fields } from "@cmssy/react";
import Component from "./src";

export const headerBlock = defineBlock({
  type: "header",
  label: "Header",
  layoutPositions: ["header"],
  component: Component as unknown as ComponentType<{ content: Record<string, unknown> }>,
  props: {
    logo: fields.media({ label: "Logo" }),
    links: fields.repeater({
      label: "Navigation",
      itemSchema: {
        label: fields.singleLine({ label: "Label" }),
        url: fields.link({ label: "URL" }),
      },
    }),
  },
});

Rendez la position dans app/layout.tsx avec CmssyServerLayout.


Style avec Tailwind v4

Les blocs sont stylisés avec Tailwind. Importez votre feuille de style globale une seule fois, puis utilisez les classes utilitaires dans vos composants.

/* styles/main.css */
@import "tailwindcss";

@theme {
  --color-background: oklch(1 0 0);
  --color-foreground: oklch(0.145 0 0);
  --color-primary: oklch(0.205 0 0);
}

Composants serveur & client

Les blocs sont des composants Next.js standard. Par défaut, ils sont rendus comme des composants serveur — zéro JS côté client. Ajoutez "use client" en haut d'un composant uniquement s'il a besoin de hooks, de gestionnaires d'événements, d'API navigateur ou d'animations côté client.

"use client";
import { useState } from "react";

export default function StatsCounter({ content }: { content: Record<string, unknown> }) {
  const [n, setN] = useState(0);
  // ...
}

Les animations de scroll / d'entrée nécessitent un composant client. Si un bloc utilise le reveal-on-scroll (framer-motion whileInView, initial={{ opacity: 0 }}, IntersectionObserver), il doit être un composant client ("use client") pour que l'animation s'exécute — sinon le HTML serveur reste à opacity:0 et le contenu n'apparaît jamais sur le site publié (tout semble correct dans l'éditeur, qui rend côté client).


Récupérer des données : loaders serveur

La plupart des blocs devraient récupérer leurs données côté serveur, pendant le SSR, via un loader. Le loader s'exécute dans CmssyServerPage et passe son résultat au composant via la prop data — le contenu arrive ainsi dans le HTML rendu côté serveur (indexable, sans flash de chargement) et les dépendances server-only n'atteignent jamais le bundle client.

import { defineBlock } from "@cmssy/react";

export const blogPostsBlock = defineBlock({
  type: "blog-posts",
  component: BlogPosts, // receives { content, context, data }
  loader: async ({ content, context }) => {
    // runs on the server only; returns RSC-serializable data
    const { loadPosts } = await import("./load-posts");
    return loadPosts({ limit: Number(content.postsPerPage) || 9 });
  },
});

Le loader ne s'exécute pas dans l'éditeur — le composant y reçoit data: undefined, donc affichez toujours un fallback raisonnable. La valeur de retour traverse la frontière serveur→client, elle doit donc être sérialisable pour RSC : objets simples, tableaux et primitives — pas de fonctions ni d'instances de classes.

Gardez les dépendances server-only ou lourdes derrière un import() dynamique dans le loader (comme ci-dessus), et protégez les helpers serveur partagés avec typeof window !== "undefined" pour qu'ils échouent bruyamment s'ils sont un jour bundlés pour le navigateur.

Appeler l'API de delivery

Utilisez createCmssyClient(...).queryScoped(...) de @cmssy/react. queryScoped injecte automatiquement workspaceId : quand votre requête déclare $workspaceId et que vous ne le passez pas, le SDK le résout à partir de votre workspaceSlug et ajoute à la fois la variable et l'en-tête x-workspace-id.

// load-posts.ts — a server-only helper, imported via dynamic import()
import { createCmssyClient } from "@cmssy/react";
import { cmssy } from "@/cmssy.config";

const client = createCmssyClient(cmssy);

export async function loadPosts(vars: { limit: number }) {
  if (typeof window !== "undefined") throw new Error("loadPosts is server-only");
  return client.queryScoped(PUBLIC_PAGES_QUERY, vars);
}

Données côté client & le context du bloc

Pour l'interactivité après le premier rendu (recherche, pagination, filtrage), initialisez l'état client à partir du data SSR et récupérez le reste côté client avec le même createCmssyClient. Le context du bloc transporte aussi des données souvent utiles sans requête :

context.locale;     // { current, default, enabled }
context.isPreview;  // true inside the editor
context.forms;      // resolved form definitions (when present)
context.auth;       // { isAuthenticated, member } when config.auth is set
context.workspace;  // { id, slug } when resolved

Voir Authentification des membres pour context.auth et le flux d'authentification sécurisé.


Prochaines étapes

Advanced Block Features - Cmssy Headless SDK