Implementation Patterns
Project Setup Guide
Two paths to get a Brease-powered Next.js site running: the CLI scaffold (recommended) or manual setup.
Path A: CLI Scaffolding (Recommended)
Run the interactive scaffolding tool:
npx brease-next
The CLI walks you through the following prompts:
- Project name — directory name for your new app
- Template — choose
starter(pre-built sections and layouts) orbase(minimal skeleton) - API token — your Brease API token from the dashboard
- Environment ID — the UUID of the Brease environment to connect to
- Default locale — e.g.
en,sk,de - Install dependencies — runs
npm installautomatically if confirmed - Initialize git — runs
git initif confirmed
Once complete you will have a fully configured project. Skip to the Verify Your Setup section below.
Path B: Manual Setup
1. Create a Next.js App
npx create-next-app@latest my-brease-app --typescript
cd my-brease-app
2. Install brease-next
npm install brease-next
3. Configure Environment Variables
Create .env.local in the project root:
BREASE_TOKEN=your_brease_api_token
BREASE_ENV=your_environment_uuid
BREASE_DEFAULT_LOCALE=en
BREASE_REVALIDATION_TIME=30
| Variable | Required | Description |
|---|---|---|
BREASE_TOKEN | Yes | API authentication token from the Brease dashboard |
BREASE_ENV | Yes | Environment UUID (e.g. production, staging) |
BREASE_DEFAULT_LOCALE | Yes | Default locale code (e.g. en, sk) |
BREASE_REVALIDATION_TIME | No | ISR revalidation interval in seconds (default 30) |
Keep secrets out of version control
Add .env.local to your .gitignore. Never commit API tokens to your repository.
4. Configure next.config.ts
Add remote image patterns for Brease media and wire up CMS-managed redirects:
// 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',
}))
},
}
export default nextConfig
5. Create the Brease Config
This file defines your section component map and context configuration:
// src/lib/brease/config.ts
import { ComponentType } from 'react'
import { SectionElementProps, BreaseContextConfig } from 'brease-next'
// Map section keys (from CMS) to React components
export const componentMap: Record<string, ComponentType<SectionElementProps>> = {
// hero: HeroSection,
// features: FeaturesSection,
}
// Context configuration — navigations and collections available globally
export const contextData: BreaseContextConfig = {
navigations: [
// { key: 'header', id: 'your-header-nav-uuid' },
// { key: 'footer', id: 'your-footer-nav-uuid' },
],
collections: [
// { key: 'faqs', id: 'your-faq-collection-uuid' },
],
userParams: {},
}
6. Create the Page Fetcher
Use React cache to deduplicate page fetches within a single render pass:
// src/lib/brease/get-page.ts
import { cache } from 'react'
import { fetchPage } from 'brease-next/server'
export const getPage = cache(async (slug: string) => {
return fetchPage(slug)
})
7. Create the Catch-All Layout
The layout wraps every page with BreaseContext, which provides navigation, collection, and locale data to all child components:
// src/app/[[...slug]]/layout.tsx
import { BreaseContext } from 'brease-next'
import { contextData } from '@/lib/brease/config'
import { getPage } from '@/lib/brease/get-page'
export default async function CatchAllLayout({
children,
params,
}: {
children: React.ReactNode
params: Promise<{ slug?: string[] }>
}) {
const { slug } = await params
const slugStr = (slug ?? []).join('/')
return (
<BreaseContext config={contextData} slug={slugStr} getPage={getPage}>
{children}
</BreaseContext>
)
}
8. Create the Catch-All Page
This page handles static generation, metadata, and rendering:
// src/app/[[...slug]]/page.tsx
import { notFound } from 'next/navigation'
import {
BreasePage,
generateBreasePageParams,
generateBreasePageMetadata,
ensureSuccess,
} from 'brease-next'
import { componentMap } from '@/lib/brease/config'
import { getPage } from '@/lib/brease/get-page'
export async function generateStaticParams() {
return generateBreasePageParams()
}
export async function generateMetadata({
params,
}: {
params: Promise<{ slug?: string[] }>
}) {
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) {
notFound()
}
const page = ensureSuccess(result)
return <BreasePage page={page} sectionMap={componentMap} />
}
9. Create a Sample Section Component
Section components are client components that receive their data via props. The props correspond to the element keys defined in the CMS:
// src/sections/hero-section.tsx
'use client'
import { BreaseImage, type BreaseMedia } from 'brease-next'
interface HeroSectionProps {
title: string
subtitle: string
backgroundImage: BreaseMedia
}
export default function HeroSection({
title,
subtitle,
backgroundImage,
}: HeroSectionProps) {
return (
<section className="relative py-24 px-6">
{backgroundImage && (
<BreaseImage breaseImage={backgroundImage} variant="xl" />
)}
<h1 className="text-5xl font-bold">{title}</h1>
{subtitle && <p className="mt-4 text-xl">{subtitle}</p>}
</section>
)
}
Then register it in your config:
// src/lib/brease/config.ts
import HeroSection from '@/sections/hero-section'
export const componentMap = {
hero: HeroSection as unknown as ComponentType<SectionElementProps>,
}
Section key matching
The key in componentMap (e.g. hero) must match the section's key field in the CMS, not its display name. You can find the key in the Brease dashboard under the section settings.
Verify Your Setup
Run the development server:
npm run dev
Open http://localhost:3000. If your Brease environment has published pages, you should see content rendered on screen. Check the terminal for any API errors.
You can also validate your environment variables programmatically:
import { validateBreaseConfig } from 'brease-next/server'
const config = validateBreaseConfig()
// Throws if BREASE_TOKEN, BREASE_ENV, or BREASE_DEFAULT_LOCALE are missing
Next Steps
- Pages & Dynamic Routes — deep dive into the catch-all route pattern
- Navigation Patterns — render header and footer navigations
- Collection Patterns — work with FAQs, team members, and other collection data