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

PropTypeRequiredDescription
configBreaseContextConfigYesDefines which navigations and collections to fetch
slugstringYesCurrent route slug. May include locale prefix (e.g. "sk/about-us")
getPageBreaseGetPageNoCached page fetcher. Recommended for deduplication
childrenReactNodeYesChild 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 in useBrease()) and an id (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:

  1. Locales -- all configured locales via fetchLocales()
  2. Page -- the current page via getPage(slug) or fetchPage(slug)
  3. Navigations -- each configured navigation via fetchNavigation(id, locale), using the locale derived from the slug
  4. Collections -- each configured collection (if any) via fetchCollectionById(id, locale)
  5. 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:

  1. generateMetadata -- to build the page title, description, and Open Graph tags
  2. Layout component -- to pass to BreaseContext for navigation context
  3. 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

PropertyTypeDescription
navigationsRecord<string, BreaseNavigation>Navigations keyed by the key you defined in config
collectionsRecord<string, BreaseCollection> | undefinedCollections keyed by config key. Undefined if none configured
availableLocalesstring[]All locale codes available on the site
localestringThe current locale, derived from the slug
userParamsanyThe userParams value from your config
pageSlugstringThe current page slug (without locale prefix)
pageBreasePageThe current page object
alternateLinksRecord<string, string>Alternate language URLs for the current page
referencesobject[]Page references
setAlternateLinks(links: Record<string, string>) => voidFunction 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

Previous
Components