Implementation Patterns
Collection Patterns
Collections in Brease represent structured, repeatable data — FAQs, team members, testimonials, partner logos, feature lists, pricing tiers, and similar content that shares the same schema across multiple entries. Collections are not pages — if content needs its own URL (like blog posts), it should be a page with a page type instead.
Accessing Collections
For collections needed across your site (e.g., in a sidebar, footer, or shared component), configure them in BreaseContextConfig:
1. Register collections in config
// src/lib/brease/config.ts
import { BreaseContextConfig } from 'brease-next'
export const contextData: BreaseContextConfig = {
navigations: [{ key: 'header', id: 'your-header-nav-uuid' }],
collections: [
{ key: 'faqs', id: 'your-faq-collection-uuid' },
{ key: 'team', id: 'your-team-collection-uuid' },
],
userParams: {},
}
Collections are also retrived from page content but they are page-scoped in this way.
2. Access in client components
'use client'
import { useBrease } from 'brease-next'
export default function FAQSection() {
const { collections } = useBrease()
const faqs = collections?.faqs
if (!faqs || faqs.entries.length === 0) return null
return (
<section className="py-16">
<h2 className="mb-8 text-3xl font-bold">Frequently Asked Questions</h2>
<dl className="space-y-6">
{faqs.entries.map((entry) => (
<div key={entry.uuid}>
<dt className="text-lg font-semibold">
{entry.elements.question as string}
</dt>
<dd className="mt-2 text-gray-600">
{entry.elements.answer as string}
</dd>
</div>
))}
</dl>
</section>
)
}
Context collections are locale-aware
BreaseContext automatically fetches collections using the locale derived from the current page slug. You do not need to pass locale manually when using the context pattern.
Typing Collection Entry Elements
Collection entry elements are typed as Record<string, unknown> by default. Define an interface matching your CMS schema and cast the elements for type safety:
import type { BreaseMedia } from 'brease-next'
interface TeamMemberElements {
name: string
role: string
bio: string // Rich text HTML
photo: BreaseMedia
linkedIn: string
}
const el = entry.elements as unknown as TeamMemberElements
This gives you full autocomplete and type checking when rendering entry data.
Fetching Collections Directly
When you need collection data outside of BreaseContext (e.g., in a server component or a route that doesn't use the catch-all layout), fetch directly:
import { fetchCollectionById } from 'brease-next/server'
const TEAM_COLLECTION_ID = 'your-team-collection-uuid'
export default async function TeamPage() {
const result = await fetchCollectionById(TEAM_COLLECTION_ID, 'en')
if (!result.success) {
return <p>Failed to load team members.</p>
}
return (
<div className="grid gap-8 md:grid-cols-3">
{result.data.entries.map((entry) => {
const el = entry.elements as unknown as TeamMemberElements
return (
<div key={entry.uuid} className="text-center">
<h3 className="text-lg font-semibold">{el.name}</h3>
<p className="text-sm text-gray-500">{el.role}</p>
</div>
)
})}
</div>
)
}
Locale parameter required
fetchCollectionById requires a locale parameter. When fetching directly (outside BreaseContext), you must supply it explicitly.
Fetching a Single Entry
Use fetchEntryById when you need a specific entry by its UUID:
import { fetchEntryById } from 'brease-next/server'
const result = await fetchEntryById(
'your-collection-uuid',
'entry-uuid-here',
'en',
)
if (result.success) {
const entry = result.data
console.log(entry.uuid, entry.name)
console.log(entry.elements)
}
Example: Testimonials Section
A complete example rendering a testimonials collection with images:
'use client'
import { useBrease, BreaseImage, type BreaseMedia } from 'brease-next'
interface TestimonialElements {
quote: string
author: string
role: string
avatar: BreaseMedia
}
export default function Testimonials() {
const { collections } = useBrease()
const testimonials = collections?.testimonials
if (!testimonials || testimonials.entries.length === 0) return null
return (
<section className="py-24">
<h2 className="mb-16 text-center text-3xl font-bold">
What Our Clients Say
</h2>
<div className="grid gap-8 md:grid-cols-2 lg:grid-cols-3">
{testimonials.entries.map((entry) => {
const el = entry.elements as unknown as TestimonialElements
return (
<blockquote key={entry.uuid} className="rounded-xl border p-6">
<p className="text-gray-700">“{el.quote}”</p>
<footer className="mt-4 flex items-center gap-3">
{el.avatar && (
<BreaseImage
breaseImage={el.avatar}
variant="sm"
className="h-10 w-10 rounded-full object-cover"
/>
)}
<div>
<p className="font-semibold">{el.author}</p>
<p className="text-sm text-gray-500">{el.role}</p>
</div>
</footer>
</blockquote>
)
})}
</div>
</section>
)
}
Multi-Locale Collections
When your site supports multiple locales, pass the locale to collection fetch functions:
import { fetchCollectionById, fetchEntryById } from 'brease-next/server'
// Fetch the Slovak version of a collection
const result = await fetchCollectionById('your-collection-uuid', 'sk')
// Fetch a specific entry in Slovak
const entry = await fetchEntryById('your-collection-uuid', 'entry-uuid', 'sk')
When using BreaseContext, locale is handled automatically based on the page slug.
Collections vs Pages
When to use which
Collections are for structured data that lives within a page — FAQs, team members, testimonials, feature lists, pricing tiers, partner logos. Entries do not have their own URL.
Pages (with page types) are for content that needs its own URL — blog posts, case studies, product pages. Use a page type with a template to standardize their structure, and reference fields for page-level metadata like author or publish date. See Templates & Page Types.
Best Practices
- Type your elements. Always create an interface for your entry elements and cast
entry.elementsto it. This catches typos and gives you IDE autocomplete. - Handle missing data gracefully. Elements can be
nullorundefinedif not filled in by the content editor. Use optional chaining and fallbacks. - Use context for global lists. If you need collection data on every page (e.g., a testimonials widget in the sidebar), configure it in
contextDataso it is fetched once byBreaseContext. - Use direct fetches for isolated pages. For pages that only need collection data in specific places, use
fetchCollectionByIddirectly in server components.
Next Steps
- Navigation Patterns — rendering menus and handling nested items
- Multi-Locale Sites — locale-aware collection fetching
- Media & Images — rendering images from collection entries