templ.js

Overview

The templjs query engine resolves values from structured input data using:

Query Resolution Flow

flowchart LR
  A[Template Expression] --> B{Path Type?}
  B -->|dot notation| C[Property Lookup]
  B -->|bracket index| D[Array / Key Access]
  B -->|variable index| E[Resolve Index Var First]
  C --> F[Resolved Value]
  D --> F
  E --> D
  F --> G{Filters?}
  G -->|yes| H[Apply Filter Chain]
  G -->|no| I[Output]
  H --> H2{More Filters?}
  H2 -->|yes| H
  H2 -->|no| I

Filter Pipeline

flowchart LR
  V[Value] --> F1["filter1()"]
  F1 --> F2["filter2(arg)"]
  F2 --> F3["filter3(a, b)"]
  F3 --> Out[Final Output]

  style V fill:#ddf,stroke:#99b
  style Out fill:#dfd,stroke:#9b9

Syntax in templates:

{{ value | upper | truncate(50) | escape }}

Source implementation: Source implementation: src/packages/core/src/query-engine/query-engine.ts

Primary behavior tests: Primary behavior tests: src/packages/core/test/query-engine/query-engine.test.ts

Dot Notation

Use . to walk nested object properties.

{{ user.profile.name }}

With input:

{
  "user": {
    "profile": {
      "name": "Alice"
    }
  }
}

Result:

Alice

Array Access

Use bracket notation for numeric indexes.

{{ items[1] }}
{
  "items": ["zero", "one", "two"]
}

Result:

one

Variable Index Access

Bracket indexes can reference another root variable.

{{ items[idx] }}
{
  "items": ["zero", "one", "two"],
  "idx": 2
}

Result:

two

Chained bracket access also works:

{{ matrix[row][col] }}

Quoted Keys

Use quoted brackets for object keys that are not valid identifiers.

{{ user["display-name"] }}

Missing Paths and Defaults

By default, unresolved paths return undefined.

Programmatic callers can provide defaultValue or enable strict mode through the query engine API.

Implementation reference: Implementation reference: src/packages/core/src/query-engine/types.ts

Filters

Apply filters with pipe syntax.

{{ user.name | upper }}
{{ total | round(2) }}
{{ tags | join(", ") }}

Built-in function catalogs:

Whitespace Control

templjs supports delimiter trim markers to remove formatting-only whitespace around expressions and statements.

Syntax contract:

Trim markers are optional. Templates without trim markers keep existing whitespace behavior.

Examples by Output Format

JSON:

{
  "items": [
    {%- for item in items -%}
    "{{- item -}}"{%- if !loop.last -%},{%- endif -%}
    {%- endfor -%}
  ]
}

Markdown:

# Release Notes
{%- for entry in entries -%}
- {{- entry.title -}}
{%- endfor -%}

HTML:

<ul>
{%- for item in items -%}
  <li>{{- item.name -}}</li>
{%- endfor -%}
</ul>

Migration Guidance

Overloads and Runtime Dispatch

Some filter names are overloaded by runtime category. For example, reverse supports both strings and arrays. The query engine selects the appropriate overload by inspecting the input value and argument shape.

Metadata and overload tests:

Expression-Based Array Filters

Array helpers such as filter and find support simple expression strings.

Examples:

{{ scores | filter("> 90") }}
{{ users | find("active == true") }}
{{ items | where("published") }}

Supported comparison forms include:

Limits and Current Behavior