Template data
Nue templates receive data from multiple sources that cascade and combine into a single context. This guide covers how data flows from YAML files, JSON files, and manipulation scripts to templates.
Data files
Any .yaml
file in your project becomes template data. Data files follow the same cascading hierarchy as configuration:
Site level - Root directory:
team.yaml # Custom site-wide data
site.yaml # Metadata and custom properties only
Example metadata and custom properties in site.yaml:
# site.yaml metadata
meta:
title: The UNIX of the web
description: Standards first web framework
og: /img/social.png
# custom properties
site_name: Acme Inc
company_email: hello@acme.com
social_links:
twitter: https://twitter.com/acme
github: https://github.com/acme
All configuration properties (site
, content
, collections
, ...) are skipped from templates data.
App level - Subdirectories:
blog/
├── app.yaml # Metadata and custom properties only
└── authors.yaml # App specific data
docs/
├── app.yaml
└── navigation.yaml
Global data
/data/
├── products.yaml
├── plans.yaml
└── social.yaml
JSON files
JSON files work identically to YAML files but are typically machine-generated from tools like TypeDoc, API documentation generators, or build processes:
{
"api_version": "2.0",
"endpoints": [
{
"path": "/users",
"method": "GET",
"description": "List all users"
}
]
}
JSON data merges with YAML data at the same hierarchy level. Properties from both file types combine into a single context.
Data manipulation scripts
Transform and enrich your data using JavaScript or TypeScript scripts. Place .js
or .ts
files in @shared/data/
directory:
/data/
├── products.yaml
├── team.yaml
└── process.js # Data manipulation script
Script signature
Export a default function that receives and returns the accumulated data:
// @shared/data/process.js
export default function(data) {
// data contains all merged YAML and JSON from this directory level
// add computed properties
data.featured_products = data.products.filter(p => p.featured)
// enrich existing data
data.team = data.team.map(member => ({
...member,
avatar_url: `/img/team/${member.avatar}`
}))
// return modified data
return data
}
Processing order
Scripts run after all YAML and JSON files at the same level are merged:
Load all
.yaml
and.json
files in the directoryMerge them into a single data object
Run any
.js
or.ts
scripts in alphabetical orderEach script receives the current merged data and returns modified data
Data compilation
Data precedence from lowest to highest priority:
Start with global data - Load all
@shared/data/*.yaml
and*.json
files, then run any.js
/.ts
scriptsAdd root-level data - From
site.yaml
and other root level .yaml filesAdd app-level data - From
app.yaml
and app-specific .yaml filesAdd page front matter - Page-specific overrides
The front matter metadata is flattened so that title
property would override meta.title
in site.yaml or app.yaml. Properties with the same name are always overridden, ie: team
property on app level would override the team array on root level.
Content collections
Collections defined in configuration become processed arrays. Each collection item includes all front matter properties plus generated metadata:
// Collection item structure
{
title: "Post Title", // from h1 or front matter
description: "Post summary", // from subtitle (h1 + p) or front matter
date: "2024-01-15", // from front matter
url: "/blog/post-slug/", // generated from file path
dir: "/blog/", // directory path
slug: "post-slug", // filename without extension
author: "Jane Doe", // from front matter
tags: ["web", "design"], // from front matter
// ... plus any other front matter properties
}
Generated properties
is_prod
-true
when site is built for productionurl
- Complete URL path generated from file path and namedir
- Directory path where the file is locatedslug
- Filename without extension, used for URL-friendly identifiersSorting applied as defined in collection configuration
Only files matching the
match
patterns are included
Content parsing
Nue automatically extracts content structure and metadata from pages:
Headings array - All headings are parsed and made available as structured data:
headings: [
{ id: "hello", text: "Hello, World", level: 1 },
{ id: "introduction", text: "Introduction", level: 2 },
{ id: "getting-started", text: "Getting started", level: 2 },
{ id: "basic-usage", text: "Basic usage", level: 3 },
{ id: "advanced-features", text: "Advanced features", level: 2 }
]
Title and description - Automatically extracted from content structure:
title
- From the first<h1>
element in the contentdescription
- From the first paragraph following an<h1>
, or from a standalone paragraph
Front matter values override these automatically parsed values.
Template context
Here's what a typical template context looks like as JSON:
{
// production flag
is_prod: false,
// flattened metadata (from meta namespace and front matter)
"title": "My Site",
"description": "Standards-first web framework",
"author": "Jane Doe",
// root-level data from YAML files
"site_name": "Acme Inc",
"company_email": "hello@acme.com",
// current page properties
"url": "/blog/my-post/",
"dir": "/blog/",
"slug": "my-post",
// parsed content structure
"headings": [
{ id: "hello", text: "Hello, World", level: 1 },
{ "id": "overview", "text": "Overview", "level": 2 },
{ "id": "features", "text": "Features", "level": 2 }
],
// built-in functions to process markdown to HTML
"markdown": function,
// team data from @shared/data/team.yaml (processed by scripts)
"team": [
{
"name": "Alice Johnson",
"role": "Lead Designer",
"avatar": "alice.jpg",
"avatar_url": "/img/team/alice.jpg"
},
{
"name": "Bob Smith",
"role": "Frontend Developer",
"avatar": "bob.jpg",
"avatar_url": "/img/team/bob.jpg"
}
],
// content collection
"blog": [
{
"title": "Design Systems at Scale",
"date": "2024-01-15",
"url": "/blog/design-systems-scale/",
"dir": "/blog/",
"slug": "design-systems-scale",
"description": "Building maintainable design systems",
"author": "Alice Johnson",
"tags": ["design", "systems"]
},
{
"title": "Web Standards First",
"date": "2024-01-10",
"url": "/blog/web-standards-first/",
"dir": "/blog/",
"slug": "web-standards-first",
"description": "Why standards matter",
"tags": ["standards", "web"]
}
]
}
Built-in functions
markdown function
Process markdown content and return HTML:
<div class="content">
{{ markdown(post.description) }}
</div>
This function is automatically available in all templates and handles the same Nuemark syntax used in content files.
Template examples
Basic template
Access any data using Nue's template syntax:
<!doctype html>
<article>
<h1>{ site_name } blog</h1>
<ul>
<li :each="post in blog">
<a href="{ post.url }">
<h2>{ post.title }</h2>
<time>{ post.date }</time>
<div>{{ markdown(post.description) }}</div>
</a>
</li>
</ul>
</article>
Table of contents component
Create a [toc]
tag using the parsed headings data:
<nav :is="toc">
<h3>Table of contents</h3>
<ul>
<li :each="heading in tocHeadings">
<a href="#{ heading.id }">{ heading.text }</a>
</li>
</ul>
<script>
// Filter to show only h2 headings
this.tocHeadings = this.headings.filter(h => h.level == 2)
</script>
</nav>
Use in Nuemark content:
[]
Lorem ipsum dolor sit amet...
More content here...
Breadcrumb navigation
Use the dir
and slug
properties for navigation:
<nav :is="breadcrumbs">
<a href="/">Home</a>
<span :if="dir != '/'">
<span>/</span>
<a href="{ dir }">{ dir.replace('/', '') }</a>
</span>
<span :if="slug">
<span>/</span>
<span>{ slug }</span>
</span>
</nav>
This automatically generates breadcrumbs like: Home / blog / my-post