### Install @cmssy/react
Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/packages/react/README.md
Install the @cmssy/react package using pnpm.
```bash
pnpm add @cmssy/react
```
--------------------------------
### Minimal Next.js Setup
Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/_autodocs/README.md
Demonstrates the minimal setup required for a Next.js application using the cmssy SDK, including configuration and page routing.
```APIDOC
## Minimal Next.js Setup
### Description
This section provides a minimal setup for a Next.js application using the cmssy SDK. It includes the necessary configuration file (`cmssy.config.ts`), the main page component (`app/[[...path]]/page.tsx`), and the draft route handler (`app/api/draft/route.ts`).
### Installation
```bash
npm install @cmssy/react @cmssy/next
```
### Configuration (`cmssy.config.ts`)
```typescript
// cmssy.config.ts
import type { CmssyNextConfig } from "@cmssy/next";
export const cmssy: CmssyNextConfig = {
apiUrl: process.env.CMSSY_API_URL!,
workspaceSlug: process.env.CMSSY_WORKSPACE_SLUG!,
draftSecret: process.env.CMSSY_DRAFT_SECRET!,
editorOrigin: process.env.CMSSY_EDITOR_ORIGIN!,
};
```
### Page Route (`app/[[...path]]/page.tsx`)
```typescript
// app/[[...path]]/page.tsx
import { createCmssyPage } from "@cmssy/next";
import { cmssy } from "@/cmssy.config";
import { blocks } from "@/cmssy/blocks";
export default createCmssyPage(cmssy, blocks);
```
### Draft Route (`app/api/draft/route.ts`)
```typescript
// app/api/draft/route.ts
import { createDraftRoute } from "@cmssy/next";
import { cmssy } from "@/cmssy.config";
export const GET = createDraftRoute(cmssy);
```
### Environment Variables
- `CMSSY_API_URL` — GraphQL API endpoint (e.g., `https://api.cmssy.io/graphql`)
- `CMSSY_WORKSPACE_SLUG` — Workspace identifier
- `CMSSY_DRAFT_SECRET` — Secret for accessing draft content (16+ chars)
- `CMSSY_EDITOR_ORIGIN` — Origin allowed to frame the app (e.g., `https://app.cmssy.io`)
- `CMSSY_AUTH_SESSION_SECRET` — Secret for encrypting session cookies (32+ chars)
```
--------------------------------
### Startup Validation Example
Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/_autodocs/configuration.md
Example of how to validate CMSSY authentication configuration during application startup. This ensures that necessary authentication settings are correctly configured before proceeding.
```typescript
// app.ts or server startup
import { assertAuthConfig } from "@cmssy/next";
if (process.env.CMSSY_ENABLE_AUTH === "true") {
assertAuthConfig(cmssy); // Throws if invalid
console.log("Auth enabled and validated");
}
```
--------------------------------
### Install cmssy SDK Packages
Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/_autodocs/README.md
Install the core React and Next.js adapter packages for the cmssy SDK.
```bash
npm install @cmssy/react @cmssy/next
```
--------------------------------
### .env.local Example
Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/_autodocs/configuration.md
An example of how to populate Cmssy configuration using environment variables in a .env.local file. This includes required and optional settings for API, workspace, draft mode, editor origin, authentication, and site URL.
```bash
CMSSY_API_URL=https://api.cmssy.io/graphql
CMSSY_WORKSPACE_SLUG=my-workspace
CMSSY_DRAFT_SECRET=your-16-or-more-character-secret-here
CMSSY_EDITOR_ORIGIN=https://app.cmssy.io
CMSSY_AUTH_SESSION_SECRET=your-32-or-more-character-secret-here
CMSSY_SITE_URL=https://example.com
```
--------------------------------
### Next.js Configuration Example
Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/_autodocs/configuration.md
Example of how to configure Cmssy SDK for a Next.js application using environment variables. Ensure all required environment variables are set.
```typescript
// cmssy.config.ts
import type { CmssyNextConfig } from "@cmssy/next";
export const cmssy: CmssyNextConfig = {
apiUrl: process.env.CMSSY_API_URL!,
workspaceSlug: process.env.CMSSY_WORKSPACE_SLUG!,
draftSecret: process.env.CMSSY_DRAFT_SECRET!,
editorOrigin: process.env.CMSSY_EDITOR_ORIGIN!,
siteUrl: "https://example.com",
defaultLocale: "en",
enabledLocales: ["en", "es", "fr"],
auth: {
modelSlug: "user",
sessionSecret: process.env.CMSSY_AUTH_SESSION_SECRET!,
},
};
```
--------------------------------
### Add @cmssy/next and @cmssy/react
Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/packages/next/README.md
Install the necessary packages for @cmssy/next and @cmssy/react using pnpm.
```bash
pnpm add @cmssy/next @cmssy/react
```
--------------------------------
### Install cmssy Next.js and React SDKs
Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/docs/getting-started/quickstart.md
Install the necessary packages for integrating cmssy with your Next.js application.
```bash
npm i @cmssy/next @cmssy/react
```
--------------------------------
### Example: Static Locale Configuration
Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/_autodocs/api-reference/locale.md
Illustrates how to configure `CmssyNextConfig` to always use a specific static locale for resolution.
```typescript
// Always use a specific locale
const cmssy: CmssyNextConfig = {
...config,
resolveLocale: () => "es",
};
```
--------------------------------
### Environment Variables Setup
Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/examples/fakturownia-invoicing/README.md
Required environment variables for integrating with cmssy and Fakturownia. Ensure these are set in your application's environment.
```env
CMSSY_WEBHOOK_SECRET=whsec_...
CMSSY_API_URL=https://api.cmssy.io
CMSSY_API_TOKEN=cs_...
FAKTUROWNIA_DOMAIN=your-account
FAKTUROWNIA_API_TOKEN=...
```
--------------------------------
### Example: Sending a Ready Message to the Editor
Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/_autodocs/api-reference/editor-bridge.md
Demonstrates how to use the `postToEditor` function to send a 'ready' message, including protocol version, block information, and schema definitions.
```typescript
import { postToEditor, PROTOCOL_VERSION } from "@cmssy/react";
postToEditor({
type: "cmssy:ready",
protocolVersion: PROTOCOL_VERSION,
blocks: [
{ id: "b1", type: "hero", bounds: { x: 0, y: 0, width: 100, height: 200 } },
],
schemas: {
hero: { title: { type: "singleLine", label: "Title" } },
},
});
```
--------------------------------
### GET /api/draft Route Handler
Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/_autodocs/api-reference/create-draft-route.md
Example of how to implement the draft route handler in your Next.js application.
```APIDOC
## GET /api/draft
### Description
This route handler enables Next.js Draft Mode preview for accessing unpublished pages.
### Method
GET
### Endpoint
`/api/draft`
### Query Parameters
- `secret` (string) - Required - The draft secret (must match `config.draftSecret`)
- `redirect` (string) - Optional - Path to redirect to (must start with `/`, defaults to `defaultRedirect`)
### Request Example
```
GET /api/draft?secret=your-secret-here&redirect=/about
```
### Response
- `307 Temporary Redirect` to the specified path with draft mode enabled.
### Security
- Secret is compared using **timing-safe comparison**.
- Redirect URL is validated to prevent open redirects (must start with `/`, cannot contain `//` or `\`, must be same-origin).
```
--------------------------------
### Render Header Layout with CmssyServerLayout
Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/_autodocs/api-reference/block-components.md
Example demonstrating how to fetch layouts and render the header section using CmssyServerLayout. Ensure fetchLayouts is configured correctly.
```typescript
import { CmssyServerLayout, fetchLayouts } from "@cmssy/react";
const layouts = await fetchLayouts(config, "/");
const headerBlocks = layouts.find(g => g.position === "header")?.blocks;
return (
);
```
--------------------------------
### CmssyEditor Component Example
Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/_autodocs/api-reference/create-cmssy-page.md
An example of how to use the CmssyEditor component in a Next.js application. This demonstrates mounting the live-edit bridge and rendering blocks based on the provided configuration.
```APIDOC
Use in your editor component to mount the live-edit bridge:
```typescript
// components/CmssyEditor.tsx
"use client";
import { CmssyLocaleProvider } from "@cmssy/react/client";
import type { CmssyEditorProps } from "@cmssy/next";
export default function CmssyEditor({
page,
locale,
defaultLocale,
edit,
}: CmssyEditorProps) {
return (
{/* Mount editor bridge here */}
{page.blocks.map((block) => {
const Component = edit.blockMap[block.type];
if (!Component) return null;
return (
);
})}
);
}
```
```
--------------------------------
### Example: User-Specific Locale Configuration
Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/_autodocs/api-reference/locale.md
Shows how to set up `CmssyNextConfig` to resolve the locale based on a logged-in user's preferences, using an asynchronous function.
```typescript
// Get locale from logged-in user's preferences
const cmssy: CmssyNextConfig = {
...config,
resolveLocale: async () => {
const user = await getCmssyUser();
return user?.preferredLocale ?? "en";
},
};
```
--------------------------------
### Example: Define a Hero Block Component
Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/_autodocs/api-reference/block-registry.md
Demonstrates how to use `defineBlock` to create a 'Hero Section' block. This example includes defining editable fields for title, subtitle, image, CTA text, and CTA link, along with an optional server-side data loader and a React component for rendering the hero section.
```typescript
import { defineBlock, fields } from "@cmssy/react";
const HeroBlock = defineBlock({
type: "hero",
label: "Hero Section",
category: "Content",
icon: "image",
props: {
title: fields.singleLine({
label: "Title",
placeholder: "Enter title",
required: true,
}),
subtitle: fields.multiLine({
label: "Subtitle",
}),
image: fields.media({
label: "Background Image",
}),
ctaText: fields.singleLine({
label: "CTA Button Label",
}),
ctaUrl: fields.link({
label: "CTA Link",
}),
},
loader: async ({ content }) => {
// Optional: fetch data server-side for this block
const imageMetadata = await fetchImageMetadata(content.image);
return { imageMetadata };
},
component: ({ content, data }) => (
),
});
export default HeroBlock;
```
--------------------------------
### Normalize Slug Examples
Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/_autodocs/api-reference/content-client.md
Examples demonstrating the usage of the normalizeSlug function with various inputs, including undefined, empty strings, single strings, and string arrays.
```typescript
import { normalizeSlug } from "@cmssy/react";
normalizeSlug(undefined);
// "/"
normalizeSlug("/");
// "/"
normalizeSlug("about");
// "/about"
normalizeSlug(["blog", "post"]);
// "/blog/post"
normalizeSlug(["", "en", "about"]);
// "/en/about"
```
--------------------------------
### Server Page Rendering Example
Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/_autodocs/api-reference/block-components.md
Example of how to use CmssyServerPage in a Next.js App Router page. It fetches page data and passes it along with block definitions and locale information to the component.
```typescript
// app/[[...path]]/page.tsx
import { CmssyServerPage, fetchPage } from "@cmssy/react";
import { blocks } from "@/cmssy/blocks";
const config = {
apiUrl: process.env.CMSSY_API_URL!,
workspaceSlug: process.env.CMSSY_WORKSPACE_SLUG!,
};
export default async function Page({ params }: { params: Promise<{ path?: string[] }> }) {
const { path } = await params;
const page = await fetchPage(config, path);
return (
);
}
```
--------------------------------
### Example Usage of fetchProduct
Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/_autodocs/api-reference/commerce.md
Demonstrates how to import and use the fetchProduct function in a Next.js application. It shows how to handle the product data and render different components based on whether the product was found.
```typescript
import { fetchProduct } from "@cmssy/next";
const product = await fetchProduct(config, "prod_123", {
populate: ["variants", "reviews"],
});
if (product) {
return ;
} else {
return ;
}
```
--------------------------------
### Mounting createCmssyAuthRoute in Next.js
Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/_autodocs/api-reference/authentication.md
Example of how to export the POST and GET methods generated by `createCmssyAuthRoute` to be used as your Next.js API route handler.
```typescript
// app/api/auth/[action]/route.ts
import { createCmssyAuthRoute } from "@cmssy/next";
import { cmssy } from "@/cmssy.config";
export const { POST, GET } = createCmssyAuthRoute(cmssy);
```
--------------------------------
### Next.js Page Component Example
Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/_autodocs/api-reference/create-cmssy-page.md
Example of how to use createCmssyPage in a Next.js App Router catch-all route (`app/[[...path]]/page.tsx`). This sets up the dynamic page rendering with optional edit mode support.
```typescript
// app/[[...path]]/page.tsx
import { createCmssyPage } from "@cmssy/next";
import { cmssy } from "@/cmssy.config";
import { blocks } from "@/cmssy/blocks";
import CmssyEditor from "@/components/CmssyEditor";
export default createCmssyPage(cmssy, blocks, {
editor: CmssyEditor,
});
```
--------------------------------
### Example Usage of resolveForms
Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/_autodocs/api-reference/forms.md
Demonstrates how to fetch a page and then resolve its associated forms. This is useful for integrating forms into your application's UI.
```typescript
import { resolveForms, fetchPage } from "@cmssy/react";
const page = await fetchPage(config, "/contact");
const forms = await resolveForms(config, page.blocks, "en", "en");
if (forms["contact-form"]) {
// Render the form in a block
}
```
--------------------------------
### Define Products Grid Block with Fields
Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/_autodocs/api-reference/fields.md
Example of defining a 'products' grid block using singleLine, select, and numeric fields. Demonstrates setting default values and specifying required fields.
```typescript
const ProductsBlock = defineBlock({
type: "products",
label: "Products Grid",
props: {
title: fields.singleLine({
label: "Section Title",
defaultValue: "Our Products",
}),
category: fields.select({
label: "Filter by Category",
options: ["All", "Electronics", "Clothing", "Home"],
defaultValue: "All",
}),
columns: fields.numeric({
label: "Grid Columns",
defaultValue: 3,
required: true,
}),
limit: fields.numeric({
label: "Max Products",
defaultValue: 12,
}),
},
component: ({ content }) => {
return (
{content.title}
{/* Fetch and render products based on content.category, content.limit */}
);
},
});
```
--------------------------------
### Example Usage of getBlockContentForLanguage
Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/_autodocs/api-reference/locale.md
Demonstrates how to use `getBlockContentForLanguage` to retrieve localized content for a specific locale, showing fallback behavior.
```typescript
// In a block loader or CmssyServerPage
import { getBlockContentForLanguage } from "@cmssy/react";
const block = {
id: "b1",
type: "hero",
content: {
title: { en: "Welcome", es: "Bienvenido" },
subtitle: "Same in all languages",
description: { en: "English only" },
},
};
const esContent = getBlockContentForLanguage(
block.content,
"es",
"en",
["en", "es"],
);
// esContent.title = "Bienvenido"
// esContent.subtitle = "Same in all languages"
// esContent.description = "English only" (fallback to any)
```
--------------------------------
### Client-Side Login Form Example
Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/_autodocs/api-reference/authentication.md
A React component for handling user login. It manages form state, submits credentials to the /api/auth/signin endpoint, and handles success or error responses.
```typescript
// components/LoginForm.tsx
"use client";
import { useState } from "react";
export default function LoginForm() {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [error, setError] = useState("");
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
const res = await fetch("/api/auth/signin", {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({ email, password }),
});
const result = await res.json();
if (result.success) {
window.location.href = "/account";
} else {
setError(result.message);
}
};
return (
);
}
```
--------------------------------
### Complete Webhook Handler
Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/_autodocs/api-reference/webhooks.md
This example demonstrates a complete webhook handler for Cmssy events using Next.js. It includes verification of the webhook signature, idempotency checks, event handling logic for different order statuses, and logging of processed events.
```APIDOC
## POST /api/webhooks/cmssy
### Description
Handles incoming webhook events from Cmssy. Verifies the signature, processes the event based on its type, and logs the outcome.
### Method
POST
### Endpoint
/api/webhooks/cmssy
### Request Headers
- **x-cmssy-signature** (string) - Required - The signature of the webhook payload.
### Request Body
(Raw request body text)
### Request Example
(See provided TypeScript example for structure)
### Response
#### Success Response (200)
- Returns 'OK' if the webhook is processed successfully.
- Returns 'Already processed' if the event has already been handled.
#### Error Response (401)
- Returns 'Verification failed' if the webhook signature is invalid.
#### Error Response (500)
- Returns 'Processing error' if an error occurs during event processing, indicating a retry might be necessary.
### Notes
- Ensure the `CMSSY_WEBHOOK_SECRET` environment variable is set.
- The `verifyCmssyWebhook` function from `@cmssy/next` is used for signature verification.
- The example includes logic for `order.paid`, `order.refunded`, and `order.fulfilled` events.
```
--------------------------------
### Define a Block with Form Integration
Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/_autodocs/api-reference/forms.md
This example shows how to define a custom block that integrates with form definitions provided via context. It demonstrates accessing form data and rendering a form dynamically.
```typescript
// Block definition
const ContactBlock = defineBlock({
type: "contact",
props: {
formId: fields.singleLine({ label: "Form" }),
},
component: ({ content, context }) => {
const form = context?.forms?.[content.formId];
if (!form) return
Form not found
;
return (
);
},
});
```
--------------------------------
### Create Draft Route Setup
Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/_autodocs/api-reference/create-draft-route.md
This snippet shows how to import and use the `createDraftRoute` function from the `@cmssy/next` package within your Next.js API route. It demonstrates passing your Cmssy configuration to the function.
```APIDOC
## GET /api/draft
### Description
This endpoint enables draft mode for content preview. It is typically called by an external editor or preview service.
### Method
GET
### Endpoint
/api/draft
### Parameters
#### Query Parameters
- **secret** (string) - Required - The secret token used to authenticate the request and enable draft mode.
- **redirect** (string) - Optional - The URL path to redirect to after enabling draft mode. If not provided, it defaults to the homepage.
### Request Example
```bash
curl "https://yoursite.com/api/draft?secret=your-16-or-more-character-secret-here&redirect=/about"
```
### Response
#### Redirect
- **307 Temporary Redirect** - Redirects the user to the specified `redirect` path with draft mode enabled.
### Error Handling
#### Invalid Secret
- **401 Unauthorized** - Returned when the provided `secret` query parameter does not match the configured draft secret.
```
GET /api/draft?secret=wrong-secret
Response: 401 Unauthorized
Body: "Invalid draft secret"
```
#### Unsafe Redirect
- **307 Temporary Redirect** - If the `redirect` URL is deemed unsafe (e.g., contains `//`, `\`, or points outside the origin), it will redirect to the homepage (`/`) instead.
```
GET /api/draft?secret=...&redirect=https://evil.com
Response: 307 to / (default, safe)
```
```
--------------------------------
### CmssyEditor Component Example
Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/_autodocs/api-reference/create-cmssy-page.md
A Next.js client component that uses CmssyLocaleProvider and renders blocks based on the edit bridge configuration. Ensure the 'use client' directive is present.
```typescript
// components/CmssyEditor.tsx
"use client";
import { CmssyLocaleProvider } from "@cmssy/react/client";
import type { CmssyEditorProps } from "@cmssy/next";
export default function CmssyEditor({
page,
locale,
defaultLocale,
edit,
}: CmssyEditorProps) {
return (
{/* Mount editor bridge here */}
{page.blocks.map((block) => {
const Component = edit.blockMap[block.type];
if (!Component) return null;
return (
);
})}
);
}
```
--------------------------------
### Define Hero Block with Fields
Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/_autodocs/api-reference/fields.md
Example of defining a 'hero' block using various field types like singleLine, multiLine, media, and link. Includes default values and required properties.
```typescript
const HeroBlock = defineBlock({
type: "hero",
label: "Hero Section",
props: {
title: fields.singleLine({
label: "Title",
required: true,
placeholder: "Enter hero title",
}),
subtitle: fields.multiLine({
label: "Subtitle",
placeholder: "Optional tagline",
}),
backgroundImage: fields.media({
label: "Background Image",
}),
ctaText: fields.singleLine({
label: "CTA Button Text",
defaultValue: "Learn More",
}),
ctaLink: fields.link({
label: "CTA Link",
}),
},
component: ({ content }) => {
const bgStyle = content.backgroundImage
? { backgroundImage: `url(${content.backgroundImage})` }
: undefined;
return (
{content.title}
{content.subtitle &&
{content.subtitle}
}
{content.ctaLink && (
{content.ctaText || "Learn More"}
)}
);
},
});
```
--------------------------------
### Example: Handling Messages from the Editor
Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/_autodocs/api-reference/editor-bridge.md
Illustrates how to use `parseEditorMessage` within a 'message' event listener to process different types of messages from the editor, such as 'select' and 'patch'.
```typescript
import { parseEditorMessage } from "@cmssy/react";
window.addEventListener("message", (event) => {
const message = parseEditorMessage(event.data);
if (!message) return;
switch (message.type) {
case "cmssy:select":
selectBlock(message.blockId);
break;
case "cmssy:patch":
updateBlockContent(message.blockId, message.content);
break;
}
});
```
--------------------------------
### Fetch Site Configuration
Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/_autodocs/api-reference/data-client.md
Fetches workspace branding and configuration, including site name, enabled languages, default language, 404 page ID, and branding details. Requires CmssyClientConfig.
```typescript
async function fetchSiteConfig(
config: CmssyClientConfig,
options?: GraphqlRequestOptions,
): Promise;
```
```typescript
import { fetchSiteConfig } from "@cmssy/react";
const siteConfig = await fetchSiteConfig(config);
console.log(siteConfig?.branding?.brandName); // "ACME Inc"
console.log(siteConfig?.enabledLanguages); // ["en", "es"]
```
--------------------------------
### Content Defaulting Best Practices
Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/docs/building-blocks/authoring-blocks.md
Illustrates the correct way to handle potentially empty content props by rendering nothing, contrasting it with an incorrect approach that fabricates content.
```tsx
// good - empty content renders nothing
return <>{heading &&
{heading}
}>;
```
```tsx
// bad - invents content the editor never wrote
const { heading = "Building the future" } = content;
```
--------------------------------
### fetchSiteConfig
Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/_autodocs/api-reference/data-client.md
Fetches the workspace branding and configuration, including site name, enabled languages, default language, 404 page ID, and branding details.
```APIDOC
## fetchSiteConfig
### Description
Fetch workspace branding and configuration.
### Method
```typescript
async function fetchSiteConfig(
config: CmssyClientConfig,
options?: GraphqlRequestOptions,
): Promise;
```
### Returns
Site name, enabled languages, default language, 404 page ID, and branding.
### Example
```typescript
import { fetchSiteConfig } from "@cmssy/react";
const siteConfig = await fetchSiteConfig(config);
console.log(siteConfig?.branding?.brandName); // "ACME Inc"
console.log(siteConfig?.enabledLanguages); // ["en", "es"]
```
```
--------------------------------
### ReadyMessage
Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/_autodocs/api-reference/editor-bridge.md
Sent when the app is ready, containing all blocks, their locations, schemas, and metadata.
```APIDOC
## ReadyMessage
Sent when the app is ready, containing all blocks, their locations, schemas, and metadata.
```typescript
interface ReadyMessage {
type: "cmssy:ready";
protocolVersion: number;
blocks: Array<{
id: string;
type: string;
bounds: BlockRect;
layoutPosition?: string;
}>;
schemas: Record;
blockMeta?: Record;
}
interface BlockRect {
x: number;
y: number;
width: number;
height: number;
}
type BlockSchema = Record;
interface BlockMeta {
label: string;
category?: string;
icon?: string;
layoutPositions?: string[];
}
```
```
--------------------------------
### createDraftRoute Function Signature
Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/_autodocs/api-reference/create-draft-route.md
The createDraftRoute function takes a configuration object and returns a Next.js GET route handler.
```APIDOC
## createDraftRoute
### Description
Creates a GET handler for the draft preview route.
### Parameters
#### Configuration (`CmssyDraftRouteConfig`)
- `draftSecret` (string) - Required - Secret for accessing draft (16+ chars)
- `defaultRedirect` (string) - Optional - Path to redirect to if none specified (defaults to "/")
### Return Type
A GET route handler function that accepts a `Request` and returns a `Promise`.
### Throws
- `Error` if `draftSecret` is less than 16 characters
- `Response(401)` if the provided secret is invalid
- `Response(500)` if secret validation fails
```
--------------------------------
### Fetch Site Configuration
Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/docs/reference/sdk-api.md
Retrieve the global site configuration. This includes settings relevant to the entire site.
```typescript
fetchSiteConfig(config) => Promise;
```
--------------------------------
### Override Locale Detection
Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/_autodocs/api-reference/create-cmssy-page.md
Set `config.resolveLocale()` to override the default locale detection mechanism. This example forces the locale to always be English.
```typescript
const cmssy = {
...config,
resolveLocale: () => "en", // Always use English
};
```
--------------------------------
### Initialize Live Editor Client
Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/packages/react/README.md
Use CmssyLazyEditor from @cmssy/react/client to enable the live editor. It loads block chunks lazily and communicates with the cmssy admin via the bridge protocol.
```tsx
"use client";
import { CmssyLazyEditor } from "@cmssy/react/client";
export const CmssyEditor = (props) => (
import("./blocks")} />
);
```
--------------------------------
### Get Authenticated User (Server Component)
Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/_autodocs/api-reference/authentication.md
Retrieve the current authenticated user in Server Components. Returns null if the user is not authenticated.
```typescript
import { getCmssyUser } from "@cmssy/next";
export default async function AccountPage() {
const user = await getCmssyUser();
if (!user) {
return
Not authenticated
;
}
return (
Welcome, {user.email}
User ID: {user.recordId}
);
}
```
--------------------------------
### CmssyAuthRouteHandlers Interface
Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/_autodocs/api-reference/authentication.md
Defines the structure for handlers that can be passed to `createCmssyAuthRoute`. It specifies the expected POST and GET methods for handling authentication requests.
```typescript
interface CmssyAuthRouteHandlers {
POST(
request: Request,
context: { params: Promise<{ action: string }> },
): Promise;
GET(
request: Request,
context: { params: Promise<{ action: string }> },
): Promise;
}
```
--------------------------------
### Local Testing with ngrok
Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/_autodocs/api-reference/webhooks.md
Use ngrok to expose your local server to the internet for testing webhook delivery. Register the ngrok URL in cmssy.
```bash
# Start ngrok tunnel
ngrok http 3000
# Register webhook in cmssy:
# https://xxxxxxxx.ngrok.io/api/webhooks/cmssy
# Test from cmssy admin panel (send test webhook)
```
--------------------------------
### Fetch Products for a Listing Page
Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/_autodocs/api-reference/commerce.md
Demonstrates how to fetch a list of published products with filtering, sorting, and population of related data for a Next.js page. Ensure you have configured the cmssy SDK with your API URL and workspace slug.
```typescript
// app/products/page.tsx
import { fetchProducts } from "@cmssy/next";
import { cmssy } from "@/cmssy.config";
const config = {
apiUrl: cmssy.apiUrl,
workspaceSlug: cmssy.workspaceSlug,
};
export default async function ProductsPage() {
const products = await fetchProducts(config, {
filter: { status: "published" },
sort: "name",
limit: 50,
populate: ["variants"],
});
return (
);
}
```
--------------------------------
### Get Locale from Headers
Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/_autodocs/api-reference/locale.md
Retrieves the current request's locale from the 'x-cmssy-locale' header, which is set by middleware. If the header is not present, it returns the default locale.
```typescript
async function getCmssyLocale(config: CmssyClientConfig): Promise;
```
```typescript
import { getCmssyLocale } from "@cmssy/next";
export async function MyServerComponent() {
const locale = await getCmssyLocale(config);
// Render with locale-specific content
}
```
--------------------------------
### Create Cmssy Client
Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/docs/reference/sdk-api.md
Instantiate the Cmssy delivery client with configuration. The `apiUrl` defaults to the Cmssy cloud if not provided.
```typescript
createCmssyClient(config: CmssyClientConfig): CmssyClient;
```
--------------------------------
### Get Session Access Token
Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/_autodocs/api-reference/authentication.md
Obtain the current session's access token, which is useful for making authenticated GraphQL queries as the logged-in user.
```typescript
import { getCmssyAccessToken } from "@cmssy/next";
import { graphqlRequest } from "@cmssy/react";
export async function fetchMyOrders(config: CmssyClientConfig) {
const token = await getCmssyAccessToken();
return graphqlRequest(
config,
MY_ORDERS_QUERY,
{},
{ headers: { authorization: `Bearer ${token}` } },
);
}
```
--------------------------------
### Configure Site URL for Canonicalization
Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/_autodocs/api-reference/seo.md
Explicitly set the `siteUrl` in your CmssyNextConfig for multi-domain setups. If omitted, the canonical URL is derived from the request host at render time.
```typescript
const cmssy: CmssyNextConfig = {
...config,
siteUrl: process.env.CMSSY_SITE_URL || "https://example.com",
};
```
--------------------------------
### Idempotent Webhook Handler Example
Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/_autodocs/api-reference/webhooks.md
Ensures webhook events are processed only once by checking for existing records before processing and creating a new record upon successful processing.
```typescript
import { db } from "@/lib/db";
async function handleOrderPaid(order: CmssyWebhookOrder, eventId: string) {
// Check if we've already processed this event
const existing = await db.webhookEvent.findUnique({
where: { id: eventId },
});
if (existing) {
console.log("Event already processed");
return;
}
// Process the order
await db.order.update({
where: { id: order.id },
data: { paymentStatus: "completed", paidAt: new Date() },
});
// Record that we processed this event
await db.webhookEvent.create({
data: { id: eventId, processedAt: new Date() },
});
}
```
--------------------------------
### Create Draft Route Handler
Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/_autodocs/api-reference/create-draft-route.md
Use this snippet to create a GET handler for your draft preview API route. Ensure you import createDraftRoute and your Cmssy configuration.
```typescript
import { createDraftRoute } from "@cmssy/next";
import { cmssy } from "@/cmssy.config";
export const GET = createDraftRoute(cmssy);
```
--------------------------------
### Create Cmssy Client
Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/_autodocs/api-reference/data-client.md
Use `createCmssyClient` to initialize a GraphQL client. Configure it with your API URL and workspace slug. This client can then be used to make queries.
```typescript
import { createCmssyClient } from "@cmssy/react";
const client = createCmssyClient({
apiUrl: "https://api.cmssy.io/graphql",
workspaceSlug: "my-workspace",
});
// Query without workspace ID
const siteConfig = await client.query(SITE_CONFIG_QUERY, {
workspaceSlug: "my-workspace",
});
// Query with automatic workspace ID resolution
const models = await client.queryScoped(MODEL_DEFINITIONS_QUERY);
```
--------------------------------
### Requesting Draft Mode from Editor
Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/_autodocs/api-reference/create-draft-route.md
Send a GET request to your draft API endpoint with the `secret` and `redirect` query parameters. The editor uses this to enable draft mode.
```bash
# Request from editor
curl "https://yoursite.com/api/draft?secret=your-16-or-more-character-secret-here&redirect=/about"
# Redirects to: /about (with draft mode enabled)
```
--------------------------------
### SITE_CONFIG_QUERY
Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/_autodocs/api-reference/data-client.md
A pre-built GraphQL query to fetch site configuration, including languages, branding, and the 404 page.
```APIDOC
## SITE_CONFIG_QUERY
### Description
Fetch site configuration (languages, branding, 404 page).
### Query
```graphql
query PublicSiteConfig($workspaceSlug: String!) {
publicSiteConfig(workspaceSlug: $workspaceSlug) {
id workspaceId siteName defaultLanguage enabledLanguages enabledFeatures
notFoundPageId previewUrl branding { brandName logoUrl faviconUrl ogImageUrl }
}
}
```
```
--------------------------------
### buildLocaleSwitchHref
Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/_autodocs/api-reference/seo.md
Builds a URL that switches to a different language while keeping the same page.
```APIDOC
## buildLocaleSwitchHref
### Description
Builds a URL that switches to a different language while keeping the same page.
### Signature
```typescript
function buildLocaleSwitchHref(
pathname: string,
fromLocale: string,
toLocale: string,
defaultLocale: string,
): string;
```
### Parameters
* **pathname** (string) - The current URL path.
* **fromLocale** (string) - The current locale.
* **toLocale** (string) - The target locale.
* **defaultLocale** (string) - The default locale of the application.
### Example
```typescript
// Current URL: /es/about
const linkToEnglish = buildLocaleSwitchHref(
"/es/about",
"es",
"en",
"en",
);
// Result: "/about"
const linkToFrench = buildLocaleSwitchHref(
"/es/about",
"es",
"fr",
"en",
);
// Result: "/fr/about"
```
```
--------------------------------
### Resolve Site Locales
Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/_autodocs/api-reference/locale.md
Fetches the workspace's enabled languages and default locale. The result is cached for 60 seconds. Use this to get locale information for your application.
```typescript
async function resolveSiteLocales(
config: CmssyClientConfig,
options?: GraphqlRequestOptions,
): Promise;
```
```typescript
interface CmssySiteLocales {
defaultLocale: string;
locales: string[];
}
```
```typescript
import { resolveSiteLocales } from "@cmssy/react";
const siteLocales = await resolveSiteLocales(config);
console.log(siteLocales.defaultLocale); // "en"
console.log(siteLocales.locales); // ["en", "es", "fr"]
```
--------------------------------
### Example Hero Block Component
Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/docs/building-blocks/authoring-blocks.md
A basic block component that accepts a `content` prop with `heading` and `showCta` fields. It renders a heading if provided and a call-to-action button if `showCta` is true.
```tsx
function Hero({
content,
}: { content: { heading?: string; showCta?: boolean }; }) {
const { heading, showCta = true } = content;
return (
{heading &&
{heading}
}
{showCta && Get started}
);
}
```
--------------------------------
### SITE_CONFIG_QUERY GraphQL
Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/_autodocs/api-reference/data-client.md
A pre-built GraphQL query to fetch site configuration, including languages, branding, and the 404 page ID.
```graphql
export const SITE_CONFIG_QUERY = `query PublicSiteConfig($workspaceSlug: String!) {
publicSiteConfig(workspaceSlug: $workspaceSlug) {
id workspaceId siteName defaultLanguage enabledLanguages enabledFeatures
notFoundPageId previewUrl branding { brandName logoUrl faviconUrl ogImageUrl }
}
}`;
```
--------------------------------
### createCmssyRobots
Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/_autodocs/api-reference/seo.md
Generates a `robots.txt` file based on workspace configuration, controlling search engine indexing.
```APIDOC
## createCmssyRobots
### Description
Generate `robots.txt` from workspace configuration. Allows or disallows indexing based on workspace settings.
### Signature
```typescript
function createCmssyRobots(config: CmssyNextConfig): () => MetadataRoute.Robots;
```
### Parameters
#### `config`
- **Type**: `CmssyNextConfig`
- **Required**: Yes
- **Description**: API configuration
### Return Type
Returns a function that generates Next.js robots metadata route handler.
### Example
```typescript
// app/robots.ts
import { createCmssyRobots } from "@cmssy/next";
import { cmssy } from "@/cmssy.config";
export default createCmssyRobots(cmssy);
```
### Generated Output Example
```
User-agent: *
Allow: /
Sitemap: https://example.com/sitemap.xml
```
```
--------------------------------
### Module Dependencies Overview
Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/_autodocs/MANIFEST.md
Visualizes the interdependencies between different modules within the CMSSY SDK, categorized into Core, Next.js, Auth, SEO, and Commerce chains.
```text
✓ README.md → INDEX.md → api-reference/ → types.md, configuration.md, errors.md
Core Chain:
block-registry ↔ fields ↔ block-components
content-client ↔ data-client ↔ forms
editor-bridge ↔ block-registry
Next.js Chain:
create-cmssy-page
├→ content-client
├→ block-registry
├→ locale
└→ forms
Auth Chain:
authentication ↔ configuration
├→ session constants
└→ cookies
SEO Chain:
seo
├→ content-client
├→ data-client
└→ locale
Commerce Chain:
commerce ↔ data-client
webhooks ↔ errors
```
--------------------------------
### Fetch Data with Generic GraphQL Client
Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/packages/react/README.md
Create a cmssy client to query the public API. Use query for raw GraphQL or queryScoped for workspace-specific queries with automatic headers and variables.
```ts
import { createCmssyClient, MODEL_RECORDS_QUERY } from "@cmssy/react";
const cmssy = createCmssyClient({ workspaceSlug: "acme" });
// raw query (you own scoping)
await cmssy.query(MY_QUERY, vars);
// workspace-scoped (auto x-workspace-id header + $workspaceId var)
const { publicModelRecords } = await cmssy.queryScoped(MODEL_RECORDS_QUERY, {
modelSlug: "posts",
});
```
--------------------------------
### Test Hero Component Rendering
Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/docs/building-blocks/authoring-blocks.md
Renders a Hero component with mock content and asserts that the heading is present in the DOM. This test uses built-in React Testing Library matchers and does not require additional setup like @testing-library/jest-dom.
```tsx
// blocks/hero/Hero.test.tsx
import { render, screen } from "@testing-library/react";
import Hero from "./Hero";
test("renders the heading when set", () => {
render();
// getByRole throws if absent, so this asserts the heading rendered
expect(screen.getByRole("heading", { name: "Hello" })).toBeTruthy();
});
test("renders nothing for an empty heading", () => {
const { container } = render();
expect(container.querySelector("h1")).toBeNull();
});
```
--------------------------------
### Provide Custom Fetch Implementation
Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/_autodocs/errors.md
When running in an environment without a global fetch implementation, pass a custom fetch function in the options.
```typescript
const page = await fetchPage(config, "/", {
fetch: (url, init) => {
// Custom fetch implementation
}
});
```
--------------------------------
### @cmssy/react/client
Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/docs/reference/sdk-api.md
Client-only helpers for the editor bridge and providers.
```APIDOC
## @cmssy/react/client
Client-only helpers for the editor bridge and providers:
- `CmssyLazyEditor`
- `CmssyLazyLayout`
- `CmssyLocaleProvider` / `useCmssyLocale`
- `useEditBridge`
- `CmssyAuthProvider` / `useCmssyUser`
- `CmssyCommerceProvider` / `useCart`
- `useCmssyOrders`
- Currency helpers (`formatPrice`, `toMinorUnits`, `fromMinorUnits`, `fractionDigits`).
```
--------------------------------
### Middleware Setup for CSP Headers
Source: https://github.com/cmssy-io/cmssy-sdk/blob/main/_autodocs/api-reference/create-cmssy-page.md
Configure your Next.js middleware to apply Content Security Policy (CSP) headers, allowing the editor to frame your application. This snippet handles detecting edit requests and setting necessary headers.
```typescript
// middleware.ts
import { NextResponse, type NextRequest } from "next/server";
import {
applyCmssyCsp,
isCmssyEditRequest,
CMSSY_EDIT_HEADER,
} from "@cmssy/next";
import { cmssy } from "@/cmssy.config";
export function middleware(request: NextRequest) {
const editMode = isCmssyEditRequest(request);
const headers = new Headers(request.headers);
headers.delete(CMSSY_EDIT_HEADER);
if (editMode) headers.set(CMSSY_EDIT_HEADER, "1");
const response = NextResponse.next({ request: { headers } });
if (editMode) applyCmssyCsp(response, { editorOrigin: cmssy.editorOrigin });
return response;
}
```