Layouts
Define page layouts with headers, footers, widths and editor preview settings, and apply them in the editor and on the frontend.
A layout is a page template: its width, wrapper classes, and optionally a header and footer. Editors choose one per page in the Page Layout setting (stored as pageLayout in the page's root props), and the same definition drives the editor preview and the frontend render.
Define layouts
import type { LayoutDefinition } from '@delmaredigital/payload-puck/layouts'
import { SiteHeader } from '@/components/header'
import { SiteFooter } from '@/components/footer'
export const siteLayouts: LayoutDefinition[] = [
{
value: 'default',
label: 'Default',
description: 'Standard page with header and footer',
maxWidth: '1200px',
header: SiteHeader,
footer: SiteFooter,
stickyHeaderHeight: 80,
},
{
value: 'landing',
label: 'Landing',
description: 'Full-width landing page',
fullWidth: true,
},
]createLayout() builds one definition with type checking, and mergeLayouts(base, custom, options?) combines lists: a custom layout replaces a base layout with the same value, exclude drops base layouts by value, and replace: true returns only your custom list.
import { createLayout, mergeLayouts, DEFAULT_LAYOUTS } from '@delmaredigital/payload-puck/layouts'
const blogLayout = createLayout({
value: 'blog',
label: 'Blog Post',
description: 'Narrow layout optimized for long-form reading',
classes: { container: 'mx-auto px-4 sm:px-6', content: 'prose prose-lg max-w-none' },
maxWidth: '720px',
})
export const siteLayouts = mergeLayouts(DEFAULT_LAYOUTS, [blogLayout])Built-in layouts
DEFAULT_LAYOUTS holds default (1200px, contained), landing (full width, no container constraints) and full-width (spans the viewport). EXTENDED_LAYOUTS adds narrow (768px) and wide (1440px). Each is also exported on its own: defaultLayout, landingLayout, fullWidthLayout, narrowLayout, wideLayout.
Layout options
Prop
Type
Use them in the editor
Pass layouts to PuckConfigProvider. The editor reads header, footer, editorBackground and editorDarkMode from them for the preview:
<PuckConfigProvider config={editorConfig} layouts={siteLayouts}>
{children}
</PuckConfigProvider>To change the options offered by the collection's Page Layout field, pass the same list to the plugin as layouts. Only value and label matter there.
createPuckPlugin({
layouts: siteLayouts,
})Use them on the frontend
PageRenderer applies the page's layout itself: it looks up the page's pageLayout in its layouts prop (default DEFAULT_LAYOUTS) and wraps the content in that layout, applying the page's header, footer, background and max-width overrides. Pass your list:
import { PageRenderer } from '@delmaredigital/payload-puck/render'
import { baseConfig } from '@delmaredigital/payload-puck/config'
import { siteLayouts } from '@/lib/puck-layouts'
<PageRenderer config={baseConfig} data={page.puckData} layouts={siteLayouts} />If pageLayout is 'default' and the list has no matching layout, PageRenderer uses the first layout. Any other unknown value falls back only to a layout named 'default'; if that is absent, no layout is applied. An empty pageLayout also applies no layout. A wrapper prop on PageRenderer takes precedence over the layout. HybridPageRenderer takes the same layouts prop.
LayoutWrapper, from /layouts, is the component that does the wrapping. Use it directly when you render Puck content some other way:
import { LayoutWrapper } from '@delmaredigital/payload-puck/layouts'
const layout = siteLayouts.find((l) => l.value === page.puckData?.root?.props?.pageLayout)
<LayoutWrapper layout={layout}>{content}</LayoutWrapper>Avoiding double headers and footers
When your app's root layout (for example Next.js layout.tsx) already renders a global header and footer, strip them from the layouts you render with. createRenderLayouts() returns the layouts without header and footer:
import { HybridPageRenderer, createRenderLayouts } from '@delmaredigital/payload-puck/render'
import { baseConfig } from '@delmaredigital/payload-puck/config'
import { siteLayouts } from '@/lib/puck-layouts'
// The host app's layout provides the header and footer
const renderLayouts = createRenderLayouts(siteLayouts)
export function PuckPage({ page }) {
return <HybridPageRenderer page={page} config={baseConfig} layouts={renderLayouts} />
}This keeps the header and footer in the editor for a realistic preview without doubling them on the live page.

