Curvea

Data and Content

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
  • @data adds page-template values to the general scope
  • @load adds explicitly loaded datasets to the general scope
  • site comes from curvea.config.json
  • structural page metadata is generated by the Engine

This means @data headline = ... is read as {{ headline }}, not {{ page.headline }}.