Block System
How blocks work in the headless model - defineBlock, instances, the component, and context.
Overview
Blocks are the building units of every cmssy page. In the headless model a block is a React component in your own Next.js repo, declared with defineBlock and a fields schema. The cmssy admin lets editors place and configure block instances; your site renders them with the SDK. A block has three parts:
- Schema (
fields) — the editable fields shown in the admin - Component — the React component that renders the content
- Registration — the block added to your
cmssy/blocks.tsarray
Defining a block
// blocks/hero/block.ts
import type { ComponentType } from "react";
import { defineBlock, fields } from "@cmssy/react";
import Component from "./src";
export const heroBlock = defineBlock({
type: "hero", // unique block type id, stored on each instance
label: "Hero",
component: Component as unknown as ComponentType<{ content: Record<string, unknown> }>,
props: {
heading: fields.singleLine({ label: "Heading" }),
body: fields.richText({ label: "Body" }),
},
});See Schema & Field Types for every field type, and Block Development for the full walkthrough.
Block instances
When an editor adds your block to a page, cmssy stores a block instance:
{
id: string; // unique UUID for this instance
type: string; // matches your block's `type` (e.g. "hero")
content: Record<string, unknown>; // language-keyed field values
}Content is stored per language ({ en: {...}, pl: {...} }); the SDK resolves the active locale before passing content to your component, so you read fields directly.
The component & context
Your component receives { content, context }:
import type { CmssyBlockContext } from "@cmssy/react";
export default function Hero({ content, context }: {
content: Record<string, unknown>;
context?: CmssyBlockContext;
}) {
const locale = context?.locale.current ?? "en";
return <section><h1>{String(content.heading ?? "")}</h1></section>;
}context is { locale: { current, default, enabled }, isPreview }. Locale-aware UI reads context.locale.enabled; isPreview is true inside the editor. For workspace data, custom models, records or forms, use createCmssyClient from @cmssy/react rather than context.
Registering blocks
// cmssy/blocks.ts
import { heroBlock } from "@/blocks/hero/block";
export const blocks = [heroBlock];That array is the single source of truth: it drives rendering, and the editor learns each block's schema over the SDK bridge, so your block appears in the admin's block picker. There is no separate block build or publish step — blocks ship when you deploy your Next.js app.
Layout blocks
Header, footer and other shared regions are layout blocks — the same as page blocks but tagged with layoutPositions (e.g. ["header"]) and rendered by CmssyServerLayout per position.
Internationalization
Content is language-keyed in the CMS and resolved per request. Routing is by path prefix (/pl/*), with the default locale using clean URLs. Read the active and enabled locales from context.locale.
Next Steps
- Block Development — full walkthrough: define, component, register, ship
- Schema & Field Types — every field type, repeaters, groups
- Advanced Features — layout blocks, styling, data fetching