Ahora con creación de páginas por IA vía el servidor MCP
Documentation

Funciones avanzadas de bloques

Bloques de layout, estilos con Tailwind, componentes de servidor vs cliente y obtención de datos con loaders de servidor.

Last updated: 29 de junio de 2026

Bloques de layout

El header, el footer y otras regiones compartidas son bloques de layout — iguales que los bloques de página, pero marcados con layoutPositions y renderizados por CmssyServerLayout por posición.

// 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" }),
      },
    }),
  },
});

Renderiza la posición en app/layout.tsx con CmssyServerLayout.


Estilos con Tailwind v4

Los bloques se estilizan con Tailwind. Importa tu hoja de estilos global una vez y luego usa clases utilitarias en tus componentes.

/* 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);
}

Componentes de servidor y cliente

Los bloques son componentes estándar de Next.js. Por defecto se renderizan como componentes de servidor — cero JS en el cliente. Añade "use client" al inicio de un componente solo cuando necesite hooks, manejadores de eventos, APIs del navegador o animaciones en el cliente.

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

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

Las animaciones de scroll / entrada necesitan un componente de cliente. Si un bloque usa reveal-on-scroll (framer-motion whileInView, initial={{ opacity: 0 }}, IntersectionObserver), debe ser un componente de cliente ("use client") para que la animación se ejecute — de lo contrario el HTML del servidor se queda en opacity:0 y el contenido nunca aparece en el sitio publicado (en el editor se ve bien, porque renderiza en el cliente).


Obtener datos: loaders de servidor

La mayoría de los bloques deberían obtener datos en el servidor, durante el SSR, mediante un loader. El loader se ejecuta en CmssyServerPage y pasa su resultado al componente como la prop data — así el contenido llega al HTML renderizado en el servidor (rastreable, sin parpadeo de carga) y las dependencias server-only nunca llegan al bundle del cliente.

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 });
  },
});

El loader no se ejecuta en el editor — allí el componente recibe data: undefined, así que renderiza siempre un fallback razonable. El valor devuelto cruza la frontera servidor→cliente, por lo que debe ser serializable para RSC: objetos planos, arrays y primitivos — sin funciones ni instancias de clases.

Mantén las dependencias server-only o pesadas detrás de un import() dinámico dentro del loader (como arriba), y protege los helpers de servidor compartidos con typeof window !== "undefined" para que fallen ruidosamente si alguna vez se empaquetan para el navegador.

Llamar a la API de delivery

Usa createCmssyClient(...).queryScoped(...) de @cmssy/react. queryScoped inyecta workspaceId automáticamente: cuando tu query declara $workspaceId y no lo pasas, el SDK lo resuelve a partir de tu workspaceSlug y añade tanto la variable como la cabecera 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);
}

Datos en el cliente y el context del bloque

Para interactividad tras el primer render (búsqueda, paginación, filtrado), inicializa el estado del cliente con el data del SSR y obtén el resto en el cliente con el mismo createCmssyClient. El context del bloque también lleva datos que a menudo necesitas sin hacer fetch:

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

Consulta Autenticación de miembros para context.auth y el flujo de autenticación seguro.


Próximos pasos

Advanced Block Features - Cmssy Headless SDK