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:

FieldTypeDescription
.pageobjectMetadata from YAML frontmatter (e.g. .page.title)
.paramsobjectURL path parameters from dynamic segments (e.g. .params.id)
.queryobjectQuery string parameters (e.g. .query.search)
.dataobjectOutput from the page's data loader (page.jq)
.childrenstringRendered 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 &lt; and &gt; 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