Templates & JQT
Grove templates use JQT -- a lightweight template syntax that embeds expressions inside double-brace delimiters (
{{ ... }}). Templates have access to page metadata, URL parameters, query strings, loaded data, and rendered child content.
Prerequisites: File-Based Routing. What you'll learn: JQT expression syntax, the template data context, YAML frontmatter, and built-in filters.
JQT Syntax
Expressions are enclosed in double braces. The dot (.) is the root of the template data context:
<h1>{{ "{{ .page.title }}" }}</h1>
<p>Welcome, user {{ "{{ .params.id }}" }}.</p>
Expressions follow jq-style path navigation. Nested fields use dot notation, for example .data.product.name.
Template Data Context
Every template receives the same top-level context object:
| Field | Type | Description |
|---|---|---|
.page | object | Metadata from YAML frontmatter (e.g. .page.title) |
.params | object | URL path parameters from dynamic segments (e.g. .params.id) |
.query | object | Query string parameters (e.g. .query.search) |
.data | object | Output from the page's data loader (page.jq) |
.children | string | Rendered HTML from the child page or nested layout |
Example context for a request to /products/42?search=term:
{
"page": { "title": "Product Detail" },
"params": { "id": "42" },
"query": { "search": "term" },
"data": { "product_id": "42", "type": "product" },
"children": "<rendered child content>"
}
YAML Frontmatter
Pages declare metadata in a YAML block delimited by --- at the top of the file. Fields become available under .page:
---
title: Home
---
<h1>{{ "{{ .page.title }}" }}</h1>
<p>Welcome to the home page.</p>
The trusted Filter
When injecting pre-rendered HTML (like .children), use the | trusted filter to prevent double-escaping:
<body>{{ "{{ .children | trusted }}" }}</body>
Without | trusted, HTML tags in the content would be escaped into visible < and > entities.
Conditionals
Use jq-style if-then-else inside expressions:
{{ "{{ if .data.items then \"has items\" else \"empty\" end }}" }}
Iteration
Use jq-style array iteration to loop over data:
{{ "{{ .data.items[] | \"<li>\" + .name + \"</li>\" }}" }}
Combining with Layouts
Layout templates use .children to wrap page content. The | trusted filter is essential here:
<html>
<head><title>{{ "{{ .page.title }}" }}</title></head>
<body>{{ "{{ .children | trusted }}" }}</body>
</html>
Nested layouts compose outward -- each layout's rendered output becomes the .children of the layout above it.
Referencing Static Assets
Templates can reference files from the public/ directory:
<link rel="stylesheet" href="/styles.css">
<script src="/app.js"></script>
See Static Assets for details on the public/ directory.
See Also
- File-Based Routing -- How files map to URLs
- Data Loaders -- Providing
.datato templates viapage.jq - Static Assets -- Serving CSS, JS, and images
- Expressions Deep Dive -- Expression syntax in Grove