id, a slug, an optional meta block for SEO, and an array of sections. The sections array is the engine of the page — it drives everything rendered on screen, in order, from top to bottom.
Page JSON structure
A page bundle is a plain JSON object. Here is the home page (src/data/pages/home.json) simplified to its skeleton:
src/data/pages/home.json (simplified)
string
required
A unique identifier for this page bundle. Used internally by the engine and Studio to address the page. Convention:
<name>-page (for example, home-page, post-detail-page).string
required
The URL path segment this page is served at. The special value
home maps to /. Dynamic collection pages use a placeholder like posts/[slug] — the engine resolves the real slug at request time.string
The
<title> tag value injected by generateMetadata. Used for SEO and browser tab labels.string
The
<meta name="description"> value. Keep it under 160 characters for best SEO results.Section[]
required
An ordered array of section objects. The visitor page renders each section in array order. See Sections for the full section object shape.
Static vs dynamic pages
Pages come in two flavours. Static pages are one-to-one mappings between a JSON file and a URL. Each file insrc/data/pages/ becomes exactly one page:
Dynamic collection pages are templates that represent one page per item in a collection. They live in a subdirectory whose name matches the collection route:
A dynamic page carries an extra
collection field that tells the engine which collection to draw from and which URL parameter holds the item key:
src/data/pages/posts/[slug].json (collection field)
slug parameter, looks up the matching item in the posts collection, and injects it wherever sections reference { "$ref": "collection:current" }.
Sections in a page
Each entry in thesections array is a section object. A section object has four fields:
string
required
A unique identifier for this section within the page. Used by Studio to target the section in the visual editor and by React as the
key prop.string
required
The section component to render. Must match a key registered in
ComponentRegistry — for example hero, posts-list, or cta-banner. See the full list in Sections.object
required
The content payload passed to the section component. Shape is validated by the section’s Zod schema. Values can be inline strings and objects, or
$ref pointers that resolve to collection data.object
Display options such as
paddingTop, paddingBottom, sticky, and showLogo. All settings are optional and section-specific. See Section settings.A cta-banner section
Page routes
The Next.js App Router catch-all route atapp/[[...slug]]/page.tsx intercepts every incoming request. It reads the URL segments, joins them into a slug string — for example posts/the-boring-stack — and looks up the matching page bundle from the page registry.
Static slugs match exactly (about). Dynamic slugs match by template key (posts/[slug]) — the engine extracts the wildcard portion and uses it for section-level $ref resolution so the correct collection item is injected into the page.
Always edit your page source files in
src/data/pages/. The framework builds the final page bundles for the visitor site as part of the standard deployment process. Changes you make to src/data/pages/ take effect on the next deploy — you never need to edit any generated or compiled output directly..png?fit=max&auto=format&n=AyzhRn9-CO-nDNc4&q=85&s=63c78e2325701880237e6cbce1399c2a)
.png?fit=max&auto=format&n=AyzhRn9-CO-nDNc4&q=85&s=0db283df6e4d31745079d6d03fd029ed)