Reuse content across pages
Manage header and footer
Render shared header and footer entities in the app layout with dedicated PageViewer components.
In this how-to, you'll learn the usual React Bricks pattern for managing a shared header and footer.
Instead of embedding them inside every page, you fetch them once in the app layout and render them with dedicated <PageViewer> components.
Docs reference: Reuse content across pages
Why use dedicated PageViewers
Header and footer are layout elements shared by the whole site.
For these global fragments, it is usually better not to use embeds inside every page for performance reasons.
Instead, render them directly in your layout with:
- one
<PageViewer>for the header - one
<PageViewer>for the footer
This is the pattern used in the starter projects scaffolded by the CLI.
Create a page type for layout entities
First, create a dedicated entity page type for layout content.
import { types } from 'react-bricks/rsc'
const pageTypes: types.IPageType[] = [
{
name: 'layout',
pluralName: 'Layout',
defaultStatus: types.PageStatus.Published,
getDefaultContent: () => [],
isEntity: true,
allowedBlockTypes: ['header', 'footer'],
},
]
export default pageTypesThis does 2 things:
isEntity: trueputs these pages under theEntitiestaballowedBlockTypesensures editors only use layout bricks there
After that, editors can create 2 entities:
headerfooter
The slug names matter because the layout will fetch those pages by slug.
Minimal app/[lang]/layout.tsx example
Here is a simplified version of a starter project pattern.
import {
PageViewer,
cleanPage,
fetchPage,
getBricks,
register,
types,
} from 'react-bricks/rsc'
import config from '@/react-bricks/config'
import '@/app/globals.css'
register(config)
const getLayoutData = async (
language: string
): Promise<{
header: types.Page | null
footer: types.Page | null
}> => {
const [header, footer] = await Promise.all([
fetchPage({ slug: 'header', language, config }).catch(() => null),
fetchPage({ slug: 'footer', language, config }).catch(() => null),
])
return { header, footer }
}
export default async function Layout({
children,
params,
}: {
children: React.ReactNode
params: Promise<{ lang: string }>
}) {
const { lang } = await params
const { header, footer } = await getLayoutData(lang)
const bricks = getBricks()
const headerOk = header
? cleanPage(header, config.pageTypes || [], bricks)
: null
const footerOk = footer
? cleanPage(footer, config.pageTypes || [], bricks)
: null
return (
<html lang={lang}>
<body>
{headerOk ? <PageViewer page={headerOk} main={false} /> : null}
<main>{children}</main>
{footerOk ? <PageViewer page={footerOk} main={false} /> : null}
</body>
</html>
)
}What this layout does
The important parts are:
fetchPageloads theheaderandfooterentitiescleanPageremoves unknown or disallowed bricks before renderingPageViewerrenders each entitymain={false}tells React Bricks these viewers are not the main page content
The children route is rendered between them:
<main>{children}</main>so every page automatically gets the same shared header and footer.
Why cleanPage is important
Before rendering a page returned by the API, the starter projects call cleanPage.
This removes content that should not be rendered, such as bricks that are unknown in the current project or not allowed by the page type configuration.
That makes the render path safer and more predictable.
If your app is not localized
This example uses app/[lang]/layout.tsx, so it receives a lang route param.
If your project is not localized, the same idea still applies.
You can just fetch with a fixed language, for example:
fetchPage({ slug: 'header', language: 'en', config })and place the same logic in your root app/layout.tsx.
Summary
For header and footer, the usual React Bricks approach is:
- store them as entities
- fetch them in the layout
- render them with dedicated
<PageViewer>components
Use this pattern for global layout fragments. Use embeds instead for reusable content blocks that editors place inside normal pages: Reuse a fragment (embed)