Skip to Content
Strapi Page Builder 1.2 is now available šŸŽ‰

<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

ParameterDescriptionRequired
configThe configuration object for the Strapi Page Builder. See Editor Configuration.true
dataThe data payload for the renderertrue
strapiThe configuration object for Strapi.false

Data payload

ParameterDescriptionRequired
templateJsonThe JSON representation of the template, returned by the Strapi Page Builder template relation field of the requested content type.true
contentThe 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

ParameterDescriptionRequired
urlThe URL of the Strapi API.true
authTokenA Strapi API token (Read-Only is enough) used to load the content referenced by Strapi fields. Used on the server only.false
imageUrlThe URL of the image server. Required if using the media field typefalse
localeThe 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.

app/[locale]/page.tsx
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:

blocks/PageBuilderConfig.tsx
"use client"; export { serverConfig as default } from "./serverConfig";

Server rendering tips

  • Client components and the puck prop. Puck gives every component a puck prop 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 in puck, including metadata (the content your blocks receive from the page), is kept untouched:

    lib/cleanProps.ts
    const 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. processProps and walkTree imported 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 processProps from /rsc in a Client Component no longer ships the editor library (@puckeditor/core) to visitors. A bundler that ignores sideEffects can import processProps from @wecre8websites/strapi-page-builder-react/process instead, 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.

Last updated on