Resources
Examples
Complete, production-ready examples showing real implementations using brease-next.
Configuration File
Central configuration for section mapping and context data.
File: src/lib/brease/config.ts
import { ComponentType } from 'react'
import { BreaseContextConfig } from 'brease-next'
import HeroSection from '@/sections/hero-section'
import SmallHeroSection from '@/sections/small-hero-section'
import TextContentSection from '@/sections/text-content-section'
import TestimonialsSection from '@/sections/testimonials-section'
type SectionProps = Record<string, unknown>
export const sectionMap: Record<string, ComponentType<SectionProps>> = {
hero: HeroSection,
smallHero: SmallHeroSection,
text: TextContentSection,
testimonials: TestimonialsSection,
}
export const contextData: BreaseContextConfig = {
navigations: [
{ key: 'mainNavigation', id: 'nav-a01c4cbb-21f7-46d5-a89c-564307998128' },
],
collections: [
{ key: 'testimonials', id: 'col-a01c8223-4e4a-40aa-90d9-70149e87322c' },
],
userParams: {},
}
Cached Page Fetcher
Deduplicate page fetches across layout, metadata, and page component.
File: src/lib/brease/get-page.ts
import { cache } from 'react'
import { fetchPage } from 'brease-next'
export const getPage = cache(async (slug: string) => fetchPage(slug))
Catch-All Layout
Layout with BreaseContext wrapping the catch-all route.
File: src/app/[[...slug]]/layout.tsx
import { ReactNode } from 'react'
import { BreaseContext } from 'brease-next'
import { contextData } from '@/lib/brease/config'
import { getPage } from '@/lib/brease/get-page'
import Header from '@/components/header'
import Footer from '@/components/footer'
export default async function SlugLayout({
children,
params,
}: {
children: ReactNode
params: Promise<{ slug?: string[] }>
}) {
const { slug } = await params
const slugStr = (slug ?? []).join('/')
return (
<BreaseContext config={contextData} slug={slugStr} getPage={getPage}>
<Header />
<main>{children}</main>
<Footer />
</BreaseContext>
)
}
Dynamic Page
Page component with static generation and metadata.
File: src/app/[[...slug]]/page.tsx
import type { Metadata } from 'next'
import {
BreasePage,
generateBreasePageParams,
generateBreasePageMetadata,
} from 'brease-next'
import { sectionMap } from '@/lib/brease/config'
import { getPage } from '@/lib/brease/get-page'
import { notFound } from 'next/navigation'
export async function generateStaticParams() {
return generateBreasePageParams()
}
export async function generateMetadata({
params,
}: {
params: Promise<{ slug?: string[] }>
}): Promise<Metadata> {
const { slug } = await params
const slugStr = (slug ?? []).join('/')
const result = await getPage(slugStr)
if (!result.success) return {}
return generateBreasePageMetadata(result.data, {
metadataBase: 'https://example.com',
})
}
export default async function Page({
params,
}: {
params: Promise<{ slug?: string[] }>
}) {
const { slug } = await params
const slugStr = (slug ?? []).join('/')
const result = await getPage(slugStr)
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} />
}
Root Layout
Root layout with site-level metadata.
File: src/app/layout.tsx
import { ReactNode } from 'react'
import type { Metadata } from 'next'
import { fetchSite } from 'brease-next'
import 'brease-next/styles'
import './globals.css'
export async function generateMetadata(): Promise<Metadata> {
const result = await fetchSite()
if (result.success) {
return {
title: {
template: `%s | ${result.data.name}`,
default: result.data.name,
},
}
}
return { title: { template: '%s', default: 'My Site' } }
}
export default function RootLayout({ children }: { children: ReactNode }) {
return (
<html lang="en">
<body>{children}</body>
</html>
)
}
Section Components
Hero Section
Full-featured hero section with responsive image.
File: src/sections/hero-section.tsx
'use client'
import { BreaseImage, type BreaseMedia } from 'brease-next'
interface HeroSectionProps {
title: string
body: string
heroMedia: BreaseMedia
}
export default function HeroSection({
title,
body,
heroMedia,
}: HeroSectionProps) {
return (
<section className="relative py-24 sm:py-32">
<div className="mx-auto max-w-7xl px-6 lg:flex lg:items-center lg:gap-x-10 lg:px-8">
<div className="mx-auto max-w-2xl lg:mx-0 lg:flex-auto">
<h1 className="text-5xl font-semibold tracking-tight sm:text-7xl">
{title}
</h1>
<div
className="mt-8 text-lg text-gray-500"
dangerouslySetInnerHTML={{ __html: body }}
/>
</div>
<div className="mt-16 lg:mt-0 lg:shrink-0 lg:grow">
{heroMedia && <BreaseImage breaseImage={heroMedia} priority />}
</div>
</div>
</section>
)
}
Testimonials Section
Section that displays collection data passed via a collection-type element.
File: src/sections/testimonials-section.tsx
'use client'
import { BreaseImage, type BreaseMedia, type BreaseCollection } from 'brease-next'
interface TestimonialsSectionProps {
title: string
testimonials: BreaseCollection
}
export default function TestimonialsSection({
title,
testimonials,
}: TestimonialsSectionProps) {
return (
<section className="py-24 sm:py-32">
<div className="mx-auto max-w-7xl px-6 lg:px-8">
<h2 className="text-4xl font-semibold tracking-tight text-center">
{title}
</h2>
<div className="mt-16 grid grid-cols-1 gap-8 sm:grid-cols-2 lg:grid-cols-3">
{testimonials.entries.map((entry) => {
const elements = entry.elements as Record<string, unknown>
const avatar = elements.avatar as BreaseMedia | undefined
return (
<blockquote key={entry.uuid} className="rounded-2xl border p-8">
<p className="text-gray-700">
“{elements.quote as string}”
</p>
<footer className="mt-6 flex items-center gap-4">
{avatar && (
<BreaseImage
breaseImage={avatar}
variant="sm"
className="h-12 w-12 rounded-full object-cover"
/>
)}
<div>
<p className="font-semibold">{elements.author as string}</p>
<p className="text-sm text-gray-500">
{elements.role as string}
</p>
</div>
</footer>
</blockquote>
)
})}
</div>
</div>
</section>
)
}
Navigation Component
Responsive navigation using the useBrease hook.
File: src/components/header.tsx
'use client'
import Link from 'next/link'
import { useState } from 'react'
import { useBrease, BreaseLink } from 'brease-next'
export default function Header() {
const [mobileMenuOpen, setMobileMenuOpen] = useState(false)
const { navigations } = useBrease()
const mainNav = navigations.mainNavigation
if (!mainNav) return null
return (
<header className="absolute inset-x-0 top-0 z-50">
<nav className="flex items-center justify-between p-6 lg:px-8">
<Link href="/" className="text-xl font-bold">
Logo
</Link>
{/* Desktop navigation */}
<div className="hidden lg:flex lg:gap-x-8">
{mainNav.items.map((item) => (
<BreaseLink
key={item.uuid}
linkData={item}
className="text-sm font-semibold"
>
{item.label}
</BreaseLink>
))}
</div>
{/* Mobile toggle */}
<button
type="button"
onClick={() => setMobileMenuOpen(!mobileMenuOpen)}
className="lg:hidden"
>
Menu
</button>
</nav>
{/* Mobile menu */}
{mobileMenuOpen && (
<div className="fixed inset-0 z-50 bg-white p-6 lg:hidden">
<button onClick={() => setMobileMenuOpen(false)}>Close</button>
<div className="mt-6 space-y-2">
{mainNav.items.map((item) => (
<BreaseLink
key={item.uuid}
linkData={item}
className="block rounded-lg px-3 py-2 text-base font-semibold"
>
{item.label}
</BreaseLink>
))}
</div>
</div>
)}
</header>
)
}
Next.js Configuration
Complete configuration with redirects, images, and CORS.
File: next.config.ts
import type { NextConfig } from 'next'
import { fetchRedirects } from 'brease-next/server'
const nextConfig: NextConfig = {
images: {
remotePatterns: [
{
protocol: 'https',
hostname: 'assets.brease.io',
pathname: '/**',
},
],
},
async redirects() {
const result = await fetchRedirects()
if (!result.success) {
console.error('Failed to fetch redirects:', result.error)
return []
}
return result.data.map((redirect) => ({
source: redirect.source,
destination: redirect.destination,
permanent: redirect.type === '301' || redirect.type === '308',
}))
},
async headers() {
return [
{
source: '/:path*',
headers: [
{
key: 'Content-Security-Policy',
value: "frame-ancestors 'self' https://app.brease.io https://dev.brease.io",
},
],
},
]
},
}
export default nextConfig
SEO Configuration
File: src/app/robots.ts
import type { MetadataRoute } from 'next'
import { generateBreaseRobots } from 'brease-next'
export default function robots(): MetadataRoute.Robots {
return generateBreaseRobots('https://example.com')
}
File: src/app/sitemap.ts
import type { MetadataRoute } from 'next'
import { generateSitemap } from 'brease-next'
export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
const result = await generateSitemap()
if (!result.success) return []
return result.data
}
Next Steps
For common issues and debugging, see Troubleshooting. For more patterns, browse the Implementation Patterns section.