Documentation
Components
The brease-next package provides React components for rendering CMS content.
Overview
The package includes three primary components:
- BreasePage - Main page renderer that maps sections to components
- BreaseImage - Optimized image component for Brease media
- BreaseContext - Server component that provides global data via context
And one client-side hook:
- useBrease() - Hook to access context data from client components
BreasePage
The main component for rendering Brease pages with dynamic sections.
Import
import { BreasePage } from 'brease-next';
Props
interface BreasePageProps {
page: BreasePageType;
sectionMap: Record<string, React.ComponentType<Record<string, unknown>>>;
}
Usage
import { fetchPage, BreasePage } from 'brease-next';
import { sectionMap } from '@/lib/brease-config';
import { notFound } from 'next/navigation';
export default async function Page() {
const result = await fetchPage('/about');
if (!result.success) {
if (result.status === 404) {
return notFound();
}
throw new Error(`Failed to load page: ${result.error}`);
}
return <BreasePage page={result.data} sectionMap={sectionMap} />;
}
How It Works
- Iterates through
page.sectionsarray - For each section, looks up component from
sectionMapusingsection.type - Spreads
section.elementsas props to the matched component - Renders the component with a unique key (
section.uuid)
Section Mapping
The sectionMap connects Brease section types to React components:
// src/lib/brease-config.ts
import { ComponentType } from 'react';
import HeroSection from '@/sections/hero-section';
import FeaturesSection from '@/sections/features-section';
import TestimonialsSection from '@/sections/testimonials-section';
type SectionProps = Record<string, unknown>;
export const sectionMap: Record<string, ComponentType<SectionProps>> = {
hero: HeroSection,
features: FeaturesSection,
testimonials: TestimonialsSection,
};
Error Handling
If a section type is not found in the map:
- A warning is logged to the console:
No component found for section type: {type} - The section is skipped (returns
null) - Other sections continue to render normally
BreaseImage
Optimized image component that wraps Next.js Image with Brease media data.
Import
import { BreaseImage } from 'brease-next';
Props
interface BreaseImageProps {
breaseImage: BreaseMedia;
className?: string;
}
Usage
import { BreaseImage, type BreaseMedia } from 'brease-next';
interface HeroSectionProps {
heroMedia: BreaseMedia;
}
export default function HeroSection({ heroMedia }: HeroSectionProps) {
return (
<div>
<h1>Welcome</h1>
<BreaseImage
breaseImage={heroMedia}
className="rounded-lg shadow-xl"
/>
</div>
);
}
Features
- Wraps Next.js
Imagecomponent with optimizations - Automatically extracts image properties from
BreaseMediaobject - Provides fallback alt text if not specified
- Handles null/undefined images gracefully
Generated Props
The component automatically maps BreaseMedia properties to Next.js Image props:
<Image
src={breaseImage.path}
alt={breaseImage.alt || breaseImage.name || 'Image alt.'}
width={breaseImage.width}
height={breaseImage.height}
className={className}
/>
Null Handling
The component returns null if breaseImage is falsy, preventing errors when media is optional in your sections.
BreaseContext
Server component that fetches global data and provides it via React Context using a configuration-driven approach.
Import
import { BreaseContext } from 'brease-next';
Props
interface BreaseContextProps {
config: {
navigations?: Array<{ key: string; id: string }>;
collections?: Array<{ key: string; id: string }>;
};
children: React.ReactNode;
}
Setup
Use BreaseContext in your root layout with configuration from brease-config.ts:
// src/app/layout.tsx
import { BreaseContext } from 'brease-next';
import { contextData } from '@/lib/brease-config';
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<body>
<BreaseContext config={contextData}>
{children}
</BreaseContext>
</body>
</html>
);
}
Configuration
Define your navigations and collections in brease-config.ts:
// src/lib/brease-config.ts
export const contextData = {
navigations: [
{ key: 'mainNavigation', id: 'nav-a01c4cbb-21f7-46d5-a89c-564307998128' },
{ key: 'footerNavigation', id: 'nav-xyz...' },
],
collections: [
{ key: 'news', id: 'col-a01c8223-4e4a-40aa-90d9-70149e87322c' },
{ key: 'products', id: 'col-abc...' },
],
};
Finding Your UUIDs:
- Open Brease dashboard
- Navigate to Navigations or Collections section
- Select your resource
- Copy the UUID from the settings or URL
What It Does
- Fetches all configured navigations and collections server-side
- Organizes data by the keys you defined in config
- Makes data available to all child components via the
useBrease()hook
Configuration Examples
Single Navigation:
export const contextData = {
navigations: [
{ key: 'mainNavigation', id: 'nav-123...' },
],
collections: [],
};
Multiple Navigations:
export const contextData = {
navigations: [
{ key: 'mainNavigation', id: 'nav-header-uuid' },
{ key: 'footerNavigation', id: 'nav-footer-uuid' },
{ key: 'sidebarNavigation', id: 'nav-sidebar-uuid' },
],
collections: [],
};
Navigations + Collections:
export const contextData = {
navigations: [
{ key: 'mainNavigation', id: 'nav-123...' },
],
collections: [
{ key: 'news', id: 'col-news-uuid' },
{ key: 'products', id: 'col-products-uuid' },
{ key: 'testimonials', id: 'col-testimonials-uuid' },
],
};
Error Handling
The component handles fetch errors gracefully:
- Logs errors to console for debugging
- Continues rendering with undefined data for failed resources
- Your components should check for undefined data when using
useBrease()
useBrease Hook
Client-side hook to access data provided by BreaseContext.
Import
import { useBrease } from 'brease-next';
Return Value
The hook returns an object with navigations and collections properties, where each is an object containing your configured resources by key:
{
navigations: {
[key: string]: BreaseNavigation | undefined;
};
collections: {
[key: string]: BreaseCollectionEntry[] | undefined;
};
}
Usage
'use client';
import { useBrease } from 'brease-next';
import Link from 'next/link';
export default function Navigation() {
const { navigations } = useBrease();
const { mainNavigation } = navigations;
if (!mainNavigation) {
return null;
}
return (
<nav>
<ul>
{mainNavigation.items.map((item, index) => (
<li key={index}>
{item.type === 'internal' && item.target ? (
<Link href={item.target.slug}>{item.value}</Link>
) : (
<a href={item.url} target="_blank" rel="noopener noreferrer">
{item.value}
</a>
)}
</li>
))}
</ul>
</nav>
);
}
Accessing Collections
'use client';
import { useBrease } from 'brease-next';
export default function LatestNews() {
const { collections } = useBrease();
const { news } = collections;
if (!news || news.length === 0) {
return null;
}
return (
<div>
<h2>Latest News</h2>
{news.slice(0, 3).map((entry) => (
<article key={entry.slug}>
<h3>{entry.elements.title as string}</h3>
<p>{entry.elements.excerpt as string}</p>
</article>
))}
</div>
);
}
Error Handling
The hook throws an error if used outside of BreaseContext:
if (context === undefined) {
throw new Error('useBrease must be used within a BreaseContext');
}
Client Component Requirement
This hook must be used in client components only (marked with 'use client').
Complete Integration Example
Here's how all components work together:
// src/lib/brease-config.ts
import HeroSection from '@/sections/hero-section';
import TextSection from '@/sections/text-section';
export const sectionMap = {
hero: HeroSection,
text: TextSection,
};
export const contextData = {
navigations: [
{ key: 'mainNavigation', id: 'nav-123...' },
],
collections: [
{ key: 'news', id: 'col-456...' },
],
};
// src/app/layout.tsx (Server Component)
import { BreaseContext } from 'brease-next';
import { contextData } from '@/lib/brease-config';
export default function RootLayout({ children }) {
return (
<html lang="en">
<body>
<BreaseContext config={contextData}>
{children}
</BreaseContext>
</body>
</html>
);
}
// src/app/page.tsx (Server Component)
import { fetchPage, BreasePage } from 'brease-next';
import { sectionMap } from '@/lib/brease-config';
export default async function Home() {
const result = await fetchPage('/');
if (!result.success) {
throw new Error(`Failed to load page: ${result.error}`);
}
return <BreasePage page={result.data} sectionMap={sectionMap} />;
}
// src/sections/hero-section.tsx (Client Component)
'use client';
import { BreaseImage, type BreaseMedia } from 'brease-next';
import Navigation from '@/components/navigation';
interface HeroSectionProps {
title: string;
body: string;
heroMedia: BreaseMedia;
}
export default function HeroSection({ title, body, heroMedia }: HeroSectionProps) {
return (
<div>
<Navigation />
<h1>{title}</h1>
<div dangerouslySetInnerHTML={{ __html: body }} />
<BreaseImage breaseImage={heroMedia} className="hero-image" />
</div>
);
}
// src/components/navigation.tsx (Client Component)
'use client';
import { useBrease } from 'brease-next';
import Link from 'next/link';
export default function Navigation() {
const { navigations } = useBrease();
const { mainNavigation } = navigations;
if (!mainNavigation) return null;
return (
<nav>
{mainNavigation.items.map((item, i) => (
<Link key={i} href={item.target?.slug || item.url || '#'}>
{item.value}
</Link>
))}
</nav>
);
}
Best Practices
Section Components
- Type your props explicitly:
interface HeroSectionProps {
title: string;
subtitle: string;
heroMedia: BreaseMedia;
}
- Handle optional fields:
export default function HeroSection({ title, subtitle, heroMedia }: HeroSectionProps) {
return (
<div>
<h1>{title}</h1>
{subtitle && <h2>{subtitle}</h2>}
{heroMedia && <BreaseImage breaseImage={heroMedia} />}
</div>
);
}
- Use client directive only when needed:
- Use
'use client'if component uses hooks, event handlers, or browser APIs - Keep server components when possible for better performance
Image Optimization
- Add appropriate sizing:
<BreaseImage
breaseImage={heroMedia}
className="w-full h-auto max-w-4xl"
/>
- Use priority for above-fold images: Extend
BreaseImageor use Next.jsImagedirectly for priority loading.
Context Usage
- Check for undefined data:
const { navigations } = useBrease();
const { mainNavigation } = navigations;
if (!mainNavigation) {
return <div>Loading navigation...</div>;
}
- Configure all resources upfront: Define all navigations and collections in
contextDatafor centralized management.
Next Steps
Now that you understand the components, follow the Implementation Guide for step-by-step instructions on building pages.