<Render /> Component
import { Render } from "@wecre8websites/strapi-page-builder-react";
import { notFound } from "next/navigation";
import config from "../../blocks/PageBuilderConfig";
const strapiConfig = {
url: process.env.NEXT_PUBLIC_API_URL as string,
authToken: process.env.STRAPI_ADMIN_TOKEN as string,
imageUrl: process.env.NEXT_PUBLIC_IMAGE_URL as string,
}
export default async function Page({ params }: { params: Promise<{ locale: string }> }) {
const { locale } = await params;
const { template, ...content } = await getCMSContent(locale)
if (!template?.json || !content) return notFound();
return <Render
config={config}
data={{
templateJson: template.json,
content
}}
strapi={{ ...strapiConfig, locale }} />;
}Props
| Parameter | Description | Required |
|---|---|---|
config | The configuration object for the Strapi Page Builder. See Editor Configuration. | true |
data | The data payload for the renderer | true |
strapi | The configuration object for Strapi. | false |
Data payload
| Parameter | Description | Required |
|---|---|---|
templateJson | The JSON representation of the template, returned by the Strapi Page Builder template relation field of the requested content type. | true |
content | The content object returned by the CMS or other external source. Required if using the processProps helper for rendering dynamic data. See Content Data for details. | true |
Strapi Configuration Object
| Parameter | Description | Required |
|---|---|---|
url | The URL of the Strapi API. | true |
authToken | A Strapi API token (Read-Only is enough) used to load the content referenced by Strapi fields. Used on the server only. | false |
imageUrl | The URL of the image server. Required if using the media field type | false |
locale | The locale of the content if a multilanguage project. | false |
The token stays on the server
When <Render /> is used from a Server Component, as above, the authToken is never sent to the browser. The browser can only ask the server to load documents that the pageās own template already references, nothing else. If you render inside a "use client" component, or in a client-only app, there is no server to hold the token: leave authToken out and resolve content on the server first (see below).
Server rendering
For the fastest pages, resolve and render everything on the server with resolveAllData and ServerRender from the /rsc entry. No component code is sent to the browser for the page itself and the token is only used server-side.
import { ServerRender, resolveAllData } from "@wecre8websites/strapi-page-builder-react/rsc";
import { notFound } from "next/navigation";
import { serverConfig } from "../../blocks/serverConfig";
const strapi = {
url: process.env.NEXT_PUBLIC_API_URL as string,
authToken: process.env.STRAPI_ADMIN_TOKEN as string,
imageUrl: process.env.NEXT_PUBLIC_IMAGE_URL as string,
}
export default async function Page({ params }: { params: Promise<{ locale: string }> }) {
const { locale } = await params;
const { template, ...content } = await getCMSContent(locale)
if (!template?.json) return notFound();
const templateJson = await resolveAllData({ ...strapi, locale }, template.json, serverConfig, content)
return <ServerRender config={serverConfig} data={{ templateJson, content }} />
}serverConfig must be importable on the server, so its file must not start with "use client" (the components it renders can still be client components). Keep your editor config, which does start with "use client", as a thin file that re-exports it:
"use client";
export { serverConfig as default } from "./serverConfig";Server rendering tips
-
Client components and the
puckprop. Puck gives every component apuckprop that contains functions (renderDropZone). Functions cannot be passed to a Client Component, so remove them before spreading props into one:<Hero {...cleanProps(data)} />. Only the functions are removed; everything else inpuck, includingmetadata(the content your blocks receive from the page), is kept untouched:lib/cleanProps.tsconst cleanProps = <T,>(props: T): T => { const puck = (props as any)?.puck; if (!puck) return props; return { ...props, puck: Object.fromEntries(Object.entries(puck).filter(([, value]) => typeof value !== "function")) }; };Slot components can only be rendered by Server Components.
-
Hydration. Pages are now HTML first, then hydrated, so anything that differs between the server and the browser (
Math.random(),Date.now(), locale-dependent dates) shows a hydration error. Derive such values from the data instead. -
Helpers on the server.
processPropsandwalkTreeimported from the main entry can be called in Server Components. -
Keep the editor out of public bundles. Since 1.2.1 the package is marked side-effect free, so importing
processPropsfrom/rscin a Client Component no longer ships the editor library (@puckeditor/core) to visitors. A bundler that ignoressideEffectscan importprocessPropsfrom@wecre8websites/strapi-page-builder-react/processinstead, which never touches the editor.
DropZones and ServerRender
Templates that still use <DropZone> render correctly with <Render />. Puckās server renderer, however, has no <DropZone> component: inside ServerRender a <DropZone> renders nothing, so its children are silently left out (this was already the case in 1.1). If you server render legacy zones, either call puck.renderDropZone({ zone: "name" }) from your component, which ServerRender provides, or, better, migrate to slot fields. Slots render the same on the server and in the editor; see Slots.