Page Data
Curvea automatically associates JSON or Markdown content data with page routes and exposes the result through page.
This is separate from @load, which explicitly loads datasets into the general template scope.
Static page JSON
For a static page, Curvea looks for matching JSON under src/data/pages.
src/pages/About.csc
src/data/pages/about.json
Nested page directories are preserved:
src/pages/company/Team.csc
src/data/pages/company/team.json
If the matching JSON file does not exist, the page still builds with structural page metadata.
Example JSON:
{
"title": "About",
"description": "About our company."
}
Template:
<h1>{{ page.title }}</h1>
<p>{{ page.description }}</p>
Dynamic JSON pages
A dynamic page uses a bracketed file name:
src/pages/products/[slug].csc
Its JSON entries come from the matching page-data directory:
src/data/pages/products/coffee.json
src/data/pages/products/tea.json
Each JSON file produces one route:
/products/coffee
/products/tea
The file name becomes the dynamic parameter and is exposed through page.params:
{{ page.params.slug }}
For [slug].csc, coffee.json therefore receives:
page.params.slug = "coffee"
A dynamic JSON page requires its matching data directory and at least one .json entry during a production build.
Structural metadata
Curvea adds these fields to every page object:
{{ page.route }}
{{ page.slug }}
{{ page.id }}
page.route is the public route beginning with /.
page.slug uses the first dynamic parameter value when one exists; otherwise it uses the normalized route path. For static nested routes, it can therefore contain more than the final URL segment.
page.id is a normalized route identifier, for example:
/ -> home
/about -> about
/blog/post -> blog-post
Structural route, slug, and id values are engine-generated after source page data is loaded.
Markdown content routes
When a page belongs to a matching collection under src/content, the content system handles that route before JSON page-data fallback.
For example:
src/pages/blog/Index.csc
src/pages/blog/[slug].csc
src/content/blog/first-post.md
The index page receives collection data. The dynamic content page receives a page object built from frontmatter plus generated content metadata such as rendered content, readingTime, route fields, params, and previous/next links.
Data boundaries
Keep these data sources distinct:
- matching JSON or Markdown content populates
page @dataadds page-template values to the general scope@loadadds explicitly loaded datasets to the general scopesitecomes fromcurvea.config.json- structural page metadata is generated by the Engine
This means @data headline = ... is read as {{ headline }}, not {{ page.headline }}.