brease-next Package
Context & Hooks
The BreaseContext server component and useBrease() hook provide a data layer that fetches navigations, page data, collections, and locales on the server and makes them available to all client components in your app.
BreaseContext
A server component that wraps your page tree, fetches shared data, and provides it via React Context.
Import
import { BreaseContext } from 'brease-next'
Props
| Prop | Type | Required | Description |
|---|---|---|---|
config | BreaseContextConfig | Yes | Defines which navigations and collections to fetch |
slug | string | Yes | Current route slug. May include locale prefix (e.g. "sk/about-us") |
getPage | BreaseGetPage | No | Cached page fetcher. Recommended for deduplication |
children | ReactNode | Yes | Child components |
BreaseContextConfig
interface BreaseContextConfig {
navigations: Array<{ key: string; id: string }>
collections?: Array<{ key: string; id: string }>
userParams: any
}
- navigations: Array of navigation resources to fetch. Each entry has a
key(used to access the navigation inuseBrease()) and anid(the navigation UUID from the CMS). - collections: Optional array of collection resources. Same key/id pattern as navigations.
- userParams: Arbitrary data passed through to client components. Useful for feature flags, configuration, or any custom data your components need.
What It Fetches
On each render, BreaseContext fetches the following data server-side:
- Locales -- all configured locales via
fetchLocales() - Page -- the current page via
getPage(slug)orfetchPage(slug) - Navigations -- each configured navigation via
fetchNavigation(id, locale), using the locale derived from the slug - Collections -- each configured collection (if any) via
fetchCollectionById(id, locale) - Alternate links -- language alternates for the current page
All fetch errors are logged to the console but do not throw. If a navigation or collection fails to load, it will be absent from the context data.
The slug Prop
The slug prop tells the context which page is currently active. Pass the raw route slug from your catch-all route params:
// src/app/[[...slug]]/layout.tsx
export default async function SlugLayout({
children,
params,
}: {
children: React.ReactNode
params: Promise<{ slug?: string[] }>
}) {
const { slug } = await params
const pageSlug = slug ? slug.join('/') : ''
return (
<BreaseContext config={contextConfig} slug={pageSlug} getPage={getPage}>
{children}
</BreaseContext>
)
}
The locale is automatically derived from the slug. For example, "sk/about-us" yields locale "sk" and page slug "about-us". A slug without a locale prefix (e.g. "about-us") uses the default locale from BREASE_DEFAULT_LOCALE.
The getPage Prop
The optional getPage prop accepts a cached page fetcher function. This is the recommended pattern because your layout, generateMetadata, and page component all need the same page data. Without caching, each would make a separate API request. With React's cache(), they share one request per render.
type BreaseGetPage = (slug: string) => Promise<BreaseResponse<BreasePage>>
If getPage is not provided, the context calls fetchPage(slug) directly.
Setup Example
// src/lib/brease/brease-config.ts
export const contextConfig = {
navigations: [
{ key: 'header', id: 'nav-a01c4cbb-21f7-46d5-a89c-564307998128' },
{ key: 'footer', id: 'nav-b12d5ebb-33f8-57e6-b90d-675418099239' },
],
collections: [
{ key: 'faqs', id: 'col-c23e6fcc-44g9-68f7-c01e-786529100340' },
],
userParams: {
showBanner: true,
},
}
// src/app/[[...slug]]/layout.tsx
import { BreaseContext } from 'brease-next'
import { contextConfig } from '@/lib/brease/brease-config'
import { getPage } from '@/lib/brease/get-page'
import 'brease-next/styles'
export default async function SlugLayout({
children,
params,
}: {
children: React.ReactNode
params: Promise<{ slug?: string[] }>
}) {
const { slug } = await params
const pageSlug = slug ? slug.join('/') : ''
return (
<BreaseContext config={contextConfig} slug={pageSlug} getPage={getPage}>
<Header />
<main>{children}</main>
<Footer />
</BreaseContext>
)
}
Cached Page Fetcher Pattern
Create a cached wrapper around fetchPage using React's cache() function:
// src/lib/brease/get-page.ts
import { cache } from 'react'
import { fetchPage } from 'brease-next'
export const getPage = cache(async (slug: string) => fetchPage(slug))
Why This Matters
In a typical Next.js App Router setup, the same page data is needed in multiple places during a single render:
generateMetadata-- to build the page title, description, and Open Graph tags- Layout component -- to pass to
BreaseContextfor navigation context - Page component -- to render with
BreasePage
Without cache(), each of these would trigger a separate HTTP request to the Brease API. With cache(), React deduplicates calls with the same arguments within a single server render, so only one request is made regardless of how many times getPage(slug) is called.
Usage Across Files
// In generateMetadata
import { getPage } from '@/lib/brease/get-page'
export async function generateMetadata({ params }) {
const { slug } = await params
const result = await getPage(slug ? slug.join('/') : '')
// ...
}
// In the page component
export default async function Page({ params }) {
const { slug } = await params
const result = await getPage(slug ? slug.join('/') : '')
// Same call, same cache -- no extra request
}
useBrease() Hook
Client-side hook that provides access to all data fetched by BreaseContext.
Import
import { useBrease } from 'brease-next'
Requirements
- Must be called inside a component wrapped by
BreaseContext - Must be used in a client component (
'use client') - Throws an error if used outside of
BreaseContext
Return Value
The hook returns a BreaseContextData object:
const {
navigations,
collections,
availableLocales,
locale,
userParams,
pageSlug,
page,
alternateLinks,
references,
setAlternateLinks,
} = useBrease()
Properties
| Property | Type | Description |
|---|---|---|
navigations | Record<string, BreaseNavigation> | Navigations keyed by the key you defined in config |
collections | Record<string, BreaseCollection> | undefined | Collections keyed by config key. Undefined if none configured |
availableLocales | string[] | All locale codes available on the site |
locale | string | The current locale, derived from the slug |
userParams | any | The userParams value from your config |
pageSlug | string | The current page slug (without locale prefix) |
page | BreasePage | The current page object |
alternateLinks | Record<string, string> | Alternate language URLs for the current page |
references | object[] | Page references |
setAlternateLinks | (links: Record<string, string>) => void | Function to update alternate links client-side |
Preview Mode
The page property updates automatically when the CMS sends live content updates in preview mode (via PostMessage). This means components using useBrease().page will re-render with the latest CMS edits without a page reload.
Usage Examples
Navigation component:
'use client'
import { useBrease, BreaseLink } from 'brease-next'
export default function Header() {
const { navigations } = useBrease()
const headerNav = navigations.header
if (!headerNav) return null
return (
<header>
<nav>
<ul>
{headerNav.items.map(item => (
<li key={item.uuid}>
<BreaseLink linkData={item}>{item.label}</BreaseLink>
</li>
))}
</ul>
</nav>
</header>
)
}
Locale switcher:
'use client'
import { useBrease } from 'brease-next'
import Link from 'next/link'
export default function LocaleSwitcher() {
const { availableLocales, locale, alternateLinks } = useBrease()
return (
<div>
{availableLocales.map(loc => (
<Link
key={loc}
href={alternateLinks[loc] || `/${loc}`}
className={loc === locale ? 'font-bold' : ''}
>
{loc.toUpperCase()}
</Link>
))}
</div>
)
}
Accessing collections:
'use client'
import { useBrease } from 'brease-next'
export default function FAQList() {
const { collections } = useBrease()
const faqs = collections?.faqs
if (!faqs) return null
return (
<dl>
<h2>Frequently Asked Questions</h2>
{faqs.entries.map(entry => (
<div key={entry.uuid}>
<dt className="font-semibold">{entry.elements.question as string}</dt>
<dd>{entry.elements.answer as string}</dd>
</div>
))}
</dl>
)
}
Using userParams:
'use client'
import { useBrease } from 'brease-next'
export default function Banner() {
const { userParams } = useBrease()
if (!userParams.showBanner) return null
return <div className="banner">Special offer!</div>
}
Next Steps
- Components -- BreasePage, BreaseImage, BreaseLink
- SEO & Metadata -- metadata generation with page data
- Preview Integration -- how live preview works with context