or run

tessl search
Log in

Version

Workspace
tessl
Visibility
Public
Created
Last updated
Describes
mavenpkg:maven/io.quarkus/quarkus-qute@3.30.x

docs

checked-templates.mdconfiguration.mdengine-advanced.mdindex.mdmessage-bundles.mdtemplate-basics.mdtemplate-extensions.mdtemplate-syntax.mdutilities.mdvalue-resolvers.md
tile.json

tessl/maven-io-quarkus--quarkus-qute

tessl install tessl/maven-io-quarkus--quarkus-qute@3.30.0

Offer templating support for web, email, etc in a build time, type-safe way

template-syntax.mddocs/

Qute Template Syntax Reference

Qute is a powerful, type-safe templating engine for the Quarkus framework. This document provides a comprehensive reference of all template syntax features, operators, section helpers, and virtual methods available in Qute templates.

Capabilities

Value Expressions

Value expressions are the fundamental building blocks of Qute templates, allowing you to output dynamic data and access object properties.

Basic Syntax:

{name}
{item.title}
{user.getFullName()}
{product.price}

Expression Types:

  • Simple values: {name} - Outputs the value of a variable
  • Property access: {user.email} - Accesses object properties via getters or fields
  • Method calls: {user.getAge()} - Invokes methods on objects
  • Nested access: {order.customer.address.city} - Chains property/method access
  • Array/List indexing: {items.0} or {items.get(0)} - Access elements by index
  • Map access: {map.keyName} or {map.get('key')} - Access map entries

Literals:

{true}
{false}
{null}
{42}
{3.14}
{'string literal'}
{"another string"}

Namespace Access:

Expressions can use namespaces to access global data or utilities:

{data:colors}
{inject:bean}
{config:propertyName}
{msg:messageKey}

Operators

Qute supports a comprehensive set of operators for expressions and conditionals.

Logical Operators

<!-- AND operator -->
{#if user.active && user.verified}
  Active and verified
{/if}

{#if condition1 and condition2}
  Both true
{/if}

<!-- OR operator -->
{#if user.admin || user.moderator}
  Has privileges
{/if}

{#if condition1 or condition2}
  At least one true
{/if}

<!-- NOT operator -->
{#if !user.banned}
  Not banned
{/if}

{#if !item.isEmpty()}
  Has items
{/if}

Comparison Operators

<!-- Equality -->
{#if age == 18}
{#if status eq 'ACTIVE'}
{#if user is admin}

<!-- Inequality -->
{#if count != 0}
{#if status ne 'INACTIVE'}

<!-- Greater than -->
{#if score > 100}
{#if value gt threshold}

<!-- Greater than or equal -->
{#if age >= 18}
{#if count ge minimum}

<!-- Less than -->
{#if price < 50}
{#if value lt maximum}

<!-- Less than or equal -->
{#if stock <= 10}
{#if count le limit}

Note: Comparison operators work with:

  • Numbers (compared numerically, converting to BigDecimal if needed)
  • Strings (compared lexicographically if same type)
  • Any Comparable objects of the same type

Arithmetic Operators

<!-- Addition -->
{count + 5}
{count.plus(5)}

<!-- Subtraction -->
{total - discount}
{total.minus(discount)}

<!-- Multiplication (via extensions) -->
{price * quantity}

<!-- Division (via extensions) -->
{total / count}

<!-- Modulo -->
{number mod 2}
{number.mod(2)}

<!-- String concatenation -->
{firstName + ' ' + lastName}
{str:concat(firstName, ' ', lastName)}

Elvis Operator (Null-Safe Default)

The elvis operator provides a default value when the expression is null or not found:

<!-- Returns 'Guest' if name is null -->
{name ?: 'Guest'}

<!-- Alternative syntax -->
{name or 'Guest'}
{name.or('Guest')}

<!-- Works with Optional -->
{optionalValue ?: 'default'}

Ternary Operator (Conditional)

<!-- Condition ? true-value : false-value -->
{user.active ? 'Active' : 'Inactive'}

<!-- Using method names -->
{user.active.ifTruthy('Active').or('Inactive')}

Null-Safe Navigation

<!-- Returns empty list if items is null -->
{items.orEmpty}

<!-- Prevents null pointer exceptions -->
{user.address.orEmpty.city}

<!-- Chain with elvis operator -->
{user.address.city ?: 'Unknown'}

Section Helpers

Section helpers are special template constructs that control flow, iteration, and template composition.

{#if} - Conditional Sections

The {#if} section allows conditional rendering based on expressions.

Basic Usage:

{#if user}
  User is logged in
{/if}

{#if count > 0}
  There are {count} items
{/if}

With {#else}:

{#if user.isPremium()}
  <div class="premium-badge">Premium Member</div>
{#else}
  <div class="standard-badge">Standard Member</div>
{/if}

With {#else if}:

{#if score >= 90}
  Grade: A
{#else if score >= 80}
  Grade: B
{#else if score >= 70}
  Grade: C
{#else}
  Grade: F
{/if}

Complex Conditions:

{#if user.active && user.verified && !user.suspended}
  Full access granted
{/if}

{#if (age >= 18 && age < 65) || hasSpecialPermission}
  Eligible
{/if}

Falsy Values:

The following values are considered "falsy" in conditions:

  • null
  • false
  • Empty strings ""
  • Empty collections
  • Empty Optional
  • Zero numbers (0, 0.0)
  • Results.NotFound

{#for} / {#each} - Iteration

The {#for} and {#each} sections iterate over collections, arrays, maps, streams, and integer ranges.

Basic Iteration:

{#for item in items}
  <li>{item.name}</li>
{/for}

{#each product in products}
  <div>{product.title} - ${product.price}</div>
{/each}

Custom Alias:

{#for user in users}
  {user.name}
{/for}

<!-- Iterating without 'in' keyword (using default 'it' alias) -->
{#each items}
  {it.name}
{/each}

Iteration Metadata:

By default, metadata is accessible with the alias followed by underscore:

{#for item in items}
  <div>
    Item #{item_count}: {item.name}
    {#if item_isFirst}(First){/if}
    {#if item_isLast}(Last){/if}
    {#if item_hasNext}, {/if}
  </div>
{/for}

Available Metadata Properties:

  • {alias_count} - 1-based iteration count (1, 2, 3, ...)
  • {alias_index} - 0-based iteration index (0, 1, 2, ...)
  • {alias_hasNext} - true if there are more elements
  • {alias_isLast} - true if this is the last element
  • {alias_isFirst} - true if this is the first element (index == 0)
  • {alias_odd} - true if count is odd
  • {alias_isOdd} - true if count is odd
  • {alias_even} - true if count is even
  • {alias_isEven} - true if count is even
  • {alias_indexParity} - "odd" or "even"

With {#else} Block:

The {#else} block executes when the iterable is empty or null:

{#for item in items}
  <li>{item.name}</li>
{#else}
  <p>No items found</p>
{/for}

Iterating Over Different Types:

<!-- Collections and Lists -->
{#each items}
  {it}
{/each}

<!-- Arrays -->
{#for element in arrayData}
  {element}
{/for}

<!-- Maps (iterates over entries) -->
{#for entry in map}
  {entry.key}: {entry.value}
{/for}

<!-- Integer range -->
{#for i in 5}
  Iteration {i}
{/for}

<!-- Long range -->
{#for n in 10L}
  Number {n}
{/for}

<!-- Streams -->
{#each stream}
  {it.data}
{/each}

<!-- Iterators -->
{#for item in iterator}
  {item}
{/for}

{#with} - Context Change

The {#with} section changes the current context object for its body.

Basic Usage:

{#with user}
  Name: {name}
  Email: {email}
  Age: {age}
{/with}

Accessing Nested Objects:

{#with order.customer}
  <h2>Customer: {name}</h2>
  {#with address}
    <p>{street}, {city}, {zipCode}</p>
  {/with}
{/with}

Note: Inside a {#with} block, this refers to the context object.

{#let} / {#set} - Variable Assignment

The {#let} and {#set} sections create local variables within their scope.

Basic Usage:

{#let name='John' age=30}
  Hello {name}, you are {age} years old.
{/let}

{#set fullName=user.firstName + ' ' + user.lastName}
  Welcome {fullName}!
{/set}

Multiple Variables:

{#let
  x=10
  y=20
  sum=x + y
}
  {x} + {y} = {sum}
{/let}

Default Values with ? Suffix:

Variables with a ? suffix only get assigned if they don't already exist or are null:

{#set greeting?='Hello'}
  {greeting}
{/set}

Without End Tag:

Both {#set} and {#let} support optional end tags:

{#set currentPage=5 /}
{#let items=data.items /}

{#include} - Template Inclusion

The {#include} section includes another template, optionally passing parameters and overriding blocks.

Basic Inclusion:

{#include 'header.html' /}

{#include template='footer.html' /}

With Parameters:

{#include 'user-card.html' user=currentUser showEmail=true /}

<!-- Parameters become available in the included template -->

Fragment Inclusion:

<!-- Include specific fragment from template -->
{#include 'common.html$navigation' /}

<!-- Include fragment from current template -->
{#include '$sidebar' /}

Dynamic Template ID:

{#include _id=templateName /}

{#include _id='templates/' + type + '.html' /}

Isolated Context:

By default, included templates have access to the parent context. Use _isolated to prevent this:

{#include 'isolated-template.html' _isolated /}

{#include 'isolated-template.html' _isolated=true data=myData /}

Unisolated (Explicit):

{#include 'template.html' _unisolated /}

With Block Overrides:

{#include 'base.html'}
  {#header}
    <h1>Custom Header</h1>
  {/header}
  {#content}
    <p>Custom content for this page</p>
  {/content}
{/include}

{#insert} - Block Insertion

The {#insert} section defines insertion points in templates that can be overridden by including templates.

In Base Template:

<!DOCTYPE html>
<html>
<head>
  {#insert title}
    <title>Default Title</title>
  {/insert}
</head>
<body>
  {#insert header}
    <h1>Default Header</h1>
  {/insert}

  {#insert content}
    <p>Default content</p>
  {/insert}

  {#insert footer}
    <footer>Default Footer</footer>
  {/insert}
</body>
</html>

In Including Template:

{#include 'base.html'}
  {#title}
    <title>My Custom Page</title>
  {/title}
  {#content}
    <h2>Welcome to My Page</h2>
    <p>This content overrides the default.</p>
  {/content}
{/include}

Custom Names:

<!-- Base template -->
{#insert name='sidebar'}
  Default sidebar content
{/insert}

<!-- Including template -->
{#include 'base.html'}
  {#sidebar}
    Custom sidebar
  {/sidebar}
{/include}

Default Block:

The main block content of {#insert} serves as the default when not overridden.

{#fragment} / {#capture} - Fragment Definition

The {#fragment} section defines reusable template fragments that can be rendered separately.

Basic Fragment:

{#fragment id='user-card'}
  <div class="card">
    <h3>{name}</h3>
    <p>{email}</p>
  </div>
{/fragment}

Accessing Fragments:

Template template = engine.getTemplate("users.html");
Fragment fragment = template.getFragment("user-card");
String rendered = fragment.data("name", "John", "email", "john@example.com").render();

Hidden/Capture Fragments:

Fragments that should not render in the main template:

{#fragment id='helper' _hidden}
  This won't render unless specifically requested
{/fragment}

{#capture id='reusable'}
  Capture is an alias for hidden fragments
{/capture}

Conditional Rendering:

{#fragment id='notification' rendered=showNotification}
  <div class="notification">{message}</div>
{/fragment}

Including Fragments:

{#include '$my-fragment' /}

{#include 'other-template.html$fragment-id' /}

{#when} / {#switch} - Pattern Matching

The {#when} (alias {#switch}) section provides pattern matching similar to switch statements.

Basic Usage:

{#when status}
  {#is 'ACTIVE'}
    <span class="badge-success">Active</span>
  {#is 'PENDING'}
    <span class="badge-warning">Pending</span>
  {#is 'INACTIVE'}
    <span class="badge-danger">Inactive</span>
  {#else}
    <span class="badge-secondary">Unknown</span>
{/when}

Alternative Syntax with {#case}:

{#switch userRole}
  {#case 'ADMIN'}
    <p>Administrator access</p>
  {#case 'MODERATOR'}
    <p>Moderator access</p>
  {#case 'USER'}
    <p>Standard user access</p>
  {#else}
    <p>Guest access</p>
{/switch}

Comparison Operators:

{#when age}
  {#is gt 65}
    Senior
  {#is ge 18}
    Adult
  {#is lt 18}
    Minor
{/when}

{#when score}
  {#is >= 90}
    Grade A
  {#is >= 80}
    Grade B
  {#is < 60}
    Grade F
{/when}

IN Operator:

{#when role}
  {#is in 'ADMIN' 'SUPERUSER' 'ROOT'}
    Administrator
  {#is in 'MODERATOR' 'EDITOR'}
    Content Manager
  {#else}
    Regular User
{/when}

NOT IN Operator:

{#when country}
  {#is !in 'US' 'CA' 'MX'}
    International shipping
  {#else}
    Domestic shipping
{/when}

{#when status}
  {#is ni 'DELETED' 'ARCHIVED'}
    Active record
{/when}

Enum Matching:

For enum values, use the constant name:

{#when orderStatus}
  {#is PENDING}
    Order is being processed
  {#is SHIPPED}
    Order has been shipped
  {#is DELIVERED}
    Order delivered
{/when}

Operators Available:

  • Equality: is, eq, == (default if no operator specified)
  • Inequality: ne, !=, not
  • Greater than: gt, >
  • Greater or equal: ge, >=
  • Less than: lt, <
  • Less or equal: le, <=
  • In set: in
  • Not in set: !in, ni

{#eval} - Dynamic Template Evaluation

The {#eval} section parses and evaluates a string as a template at runtime.

Basic Usage:

{#eval template='Hello {name}!' /}

{#set dynamicTemplate='<p>Welcome {user.name}!</p>' /}
{#eval template=dynamicTemplate /}

With Parameters:

{#eval template='Hello {firstName} {lastName}!' firstName='John' lastName='Doe' /}

Template from Variable:

{#eval template=templateString param1=value1 param2=value2 /}

Use Cases:

  • Dynamic email templates stored in database
  • User-customizable templates
  • Template strings from configuration
  • A/B testing with template variants

Note: The evaluated template string is parsed by the Qute engine and has access to all passed parameters.

Comments

Comments are ignored during rendering and can be used for documentation or temporarily disabling code.

Single-line Comments:

{! This is a comment !}

{! TODO: Add error handling here !}

Multi-line Comments:

{!
  This is a multi-line comment
  that spans several lines
  and will not be rendered
!}

Commenting Out Code:

{!
{#if user}
  <div>This code is temporarily disabled</div>
{/if}
!}

Note: Comments are completely removed during parsing and do not appear in the rendered output.

Escaping and Raw Output

By default, Qute escapes output based on the template variant (HTML, XML, etc.) to prevent XSS attacks.

Default Escaping (HTML):

{message}
<!-- If message = "<script>alert('xss')</script>" -->
<!-- Output: &lt;script&gt;alert('xss')&lt;/script&gt; -->

Raw/Unsafe Output:

Use the raw or safe virtual methods to output unescaped content:

{htmlContent.raw}

{htmlContent.safe}

{htmlContent|raw}

{htmlContent|safe}

Example:

{#let html='<strong>Bold text</strong>'}
  Escaped: {html}
  <!-- Output: &lt;strong&gt;Bold text&lt;/strong&gt; -->

  Raw: {html.raw}
  <!-- Output: <strong>Bold text</strong> -->
{/let}

Using RawString Class:

// In Java code
template.data("content", new RawString("<p>Safe HTML</p>"));

Warning: Only use .raw or .safe with trusted content to avoid XSS vulnerabilities.

Virtual Methods

Virtual methods are special methods available on all objects in templates, providing utility functions and transformations.

orEmpty

Returns an empty list if the value is null or not found:

{#for item in items.orEmpty}
  {item.name}
{#else}
  No items
{/for}

<!-- Prevents errors when items is null -->
{items.orEmpty.size}

ifTruthy

Returns the first parameter if the base is truthy, otherwise returns NotFound (useful with elvis operator):

{user.active.ifTruthy('Yes').or('No')}

{count.ifTruthy('Has items').or('Empty')}

<!-- Ternary operator alternative -->
{user.isPremium() ? 'Premium' : 'Standard'}

or (Elvis Operator Method)

Provides a default value:

{name.or('Unknown')}

{user.email.or('no-email@example.com')}

{optionalValue.or(defaultValue)}

this

Returns the object itself:

{#with user}
  User object: {this}
  Name: {name}
{/with}

raw / safe

Outputs content without escaping:

{htmlContent.raw}

{markup.safe}

Collection Methods

size - Returns collection size:

{items.size}

{#if users.size > 10}Too many users{/if}

isEmpty / empty - Checks if collection is empty:

{#if items.isEmpty}
  No items
{/if}

{#if !cart.empty}
  Cart has items
{/if}

contains - Checks if collection contains element:

{#if roles.contains('ADMIN')}
  Admin user
{/if}

List Methods

get(index) - Gets element at index:

{items.get(0)}

{users.get(selectedIndex)}

Index access (via extension):

{items.0}
{items.1}
{list.42}

first - Gets first element:

{items.first}

{#if list.first.name == 'Alpha'}First item is Alpha{/if}

last - Gets last element:

{items.last}

{users.last.email}

reversed - Returns reverse iterator:

{#for item in items.reversed}
  {item.name}
{/for}

take(n) - Returns first n elements:

{#for item in items.take(5)}
  {item}
{/for}

takeLast(n) - Returns last n elements:

{#for item in items.takeLast(3)}
  {item}
{/for}

Map Methods

keys / keySet - Returns map keys:

{#for key in map.keys}
  {key}
{/for}

values - Returns map values:

{#for value in map.values}
  {value}
{/for}

entrySet - Returns map entries:

{#for entry in map.entrySet}
  {entry.key}: {entry.value}
{/for}

size - Returns map size:

{map.size}

empty / isEmpty - Checks if map is empty:

{#if !map.empty}
  Map has entries
{/if}

get(key) - Gets value for key:

{map.get('username')}

containsKey(key) - Checks key existence:

{#if map.containsKey('email')}
  Email exists
{/if}

Map.Entry Methods

key - Returns entry key:

{entry.key}

value - Returns entry value:

{entry.value}

String Methods (via Extensions)

Concatenation:

{firstName + ' ' + lastName}

{str:concat(firstName, ' ', lastName)}

Format:

{'Hello %s'.fmt(name)}

{str:fmt('Total: $%.2f', price)}

Join:

{str:join(', ', item1, item2, item3)}

Number Methods (via Extensions)

Arithmetic:

{count.plus(5)}
{count + 5}

{total.minus(discount)}
{total - discount}

{value.mod(2)}

Time Formatting (via time: namespace)

{time:format(now, 'yyyy-MM-dd')}

{time:format(timestamp, 'HH:mm:ss', locale)}

{now.format('yyyy-MM-dd HH:mm')}

Special Variables and Keywords

this

Refers to the current context object:

{#with user}
  {this.name}
  {name}
{/with}

inject

Access CDI beans (Quarkus integration):

{inject:beanName.method()}

config

Access configuration properties (Quarkus integration):

{config:propertyName}

{config:['app.version']}

{config:property('database.url')}

msg

Access internationalized messages (Quarkus integration):

{msg:greeting}

{msg:welcomeMessage(user.name)}

data

Default namespace for template data:

{data:items}

{data:user.name}

Type-Safe Templates

Qute supports type-safe templates in Quarkus applications using compile-time validation.

Parameter Declarations:

{@org.acme.User user}
{@java.lang.String title}
{@java.util.List<org.acme.Item> items}

<!DOCTYPE html>
<html>
<head><title>{title}</title></head>
<body>
  <h1>Welcome {user.name}!</h1>
  {#for item in items}
    <div>{item.title}</div>
  {/for}
</body>
</html>

@CheckedTemplate Annotation:

@CheckedTemplate
public class Templates {
    public static native TemplateInstance users(List<User> users);
    public static native TemplateInstance product(Product product, boolean showDetails);
}

// Usage
Templates.users(userList).render();

Benefits:

  • Compile-time validation of expressions
  • IDE autocompletion
  • Refactoring support
  • Type safety guarantees

Advanced Expression Features

Nested Access with Null Safety

{user.address.city.orEmpty}

{user?.address?.city ?: 'Unknown'}

{customer.orders.first.items.size}

Method Parameters

{user.hasRole('ADMIN')}

{product.calculatePrice(quantity, discount)}

{string.substring(0, 10)}

Named Arguments (Java side)

// Template method with named parameters
public native TemplateInstance greet(String name, String title);
{greet(name='John', title='Mr.')}

Composed Expressions

{(x + y) * z}

{items.size > 0 && items.first.active}

{user.role == 'ADMIN' || user.role == 'MODERATOR'}

Whitespace Control

Qute automatically removes standalone lines (lines containing only section tags) to produce clean output.

Standalone Lines Removed:

{#if user}
  Content here
{/if}

Output has no extra blank lines from the tag lines.

Disable Standalone Line Removal:

Configure the engine:

Engine.builder()
    .removeStandaloneLines(false)
    .build();

Or in Quarkus configuration:

quarkus.qute.remove-standalone-lines=false

Template Variants

Templates can have variants based on content type, locale, and encoding.

Setting Variant:

template.instance()
    .setVariant(Variant.forContentType("text/html"))
    .render();

Content Types:

  • text/html - HTML with automatic escaping
  • text/plain - Plain text, no escaping
  • text/xml - XML with escaping
  • application/json - JSON format

Locale Support:

template.instance()
    .setLocale(Locale.FRENCH)
    .render();

Error Handling

Qute provides detailed error messages with template locations.

Common Errors:

  • Property not found: When accessing non-existent property
  • Method not found: When calling non-existent method
  • Type mismatch: When operations don't match types
  • Iteration error: When iterating non-iterable objects

Strict vs. Lenient Rendering:

By default, Qute uses strict rendering (throws exceptions). Configure lenient mode:

Engine.builder()
    .strictRendering(false)
    .build();

Using orEmpty to Prevent Errors:

{#for item in maybeNullList.orEmpty}
  {item}
{/for}

Common Pitfalls and Solutions

Pitfall 1: Forgetting Elvis Operator for Nulls

<!-- Wrong: Throws exception if name is null -->
{name}

<!-- Correct: Provides default for null -->
{name ?: 'Guest'}

Pitfall 2: Iterating Potentially Null Collections

<!-- Wrong: Throws exception if items is null -->
{#for item in items}
  {item}
{/for}

<!-- Correct: Use orEmpty -->
{#for item in items.orEmpty}
  {item}
{#else}
  No items found
{/for}

Pitfall 3: Incorrect Operator Precedence

<!-- Wrong: May not evaluate as expected -->
{#if user.active && user.verified || user.admin}
  <!-- Evaluates as: (user.active && user.verified) || user.admin -->
{/if}

<!-- Correct: Use parentheses for clarity -->
{#if (user.active && user.verified) || user.admin}
  Access granted
{/if}

Pitfall 4: Escaping Trusted HTML

<!-- Wrong: HTML is escaped -->
{htmlContent}
<!-- Output: &lt;strong&gt;Bold&lt;/strong&gt; -->

<!-- Correct: Use raw for trusted content -->
{htmlContent.raw}
<!-- Output: <strong>Bold</strong> -->

Pitfall 5: Using Wrong Section for Pattern Matching

<!-- Wrong: Nested if statements -->
{#if status == 'ACTIVE'}
  Active
{#else}
  {#if status == 'PENDING'}
    Pending
  {#else}
    Inactive
  {/if}
{/if}

<!-- Correct: Use when/switch -->
{#when status}
  {#is 'ACTIVE'}Active{/is}
  {#is 'PENDING'}Pending{/is}
  {#else}Inactive{/else}
{/when}

Edge Cases

Empty String vs Null

{! Empty strings are falsy !}
{#if emptyString}
  Won't render
{/if}

{! But different from null !}
{emptyString ?: 'default'}  {! Renders empty string, not 'default' !}
{nullValue ?: 'default'}    {! Renders 'default' !}

Zero Values

{! Zero is falsy in conditions !}
{#if count}
  Has items
{#else}
  No items  {! Renders when count is 0 !}
{/if}

{! But zero is a valid value !}
{count ?: 'N/A'}  {! Renders '0', not 'N/A' !}

Method Call vs Property Access

{! Both work for getters !}
{user.getName()}  {! Method call !}
{user.name}       {! Property access (calls getName()) !}

{! For methods with parameters, must use call syntax !}
{user.hasRole('ADMIN')}  {! Must use () !}

Nested Fragment Access

{! Access fragment from current template !}
{#include '$myFragment' /}

{! Access fragment from other template !}
{#include 'other-template.html$myFragment' /}

{! Pass parameters to fragment !}
{#include '$myFragment' param1=value1 param2=value2 /}

Complex Expressions in Sections

{! Expressions in section parameters are evaluated !}
{#if items.size > (limit * 2)}
  Too many items
{/if}

{#for item in items.take(maxItems)}
  {item}
{/for}

{#let calculated=price * quantity * (1 + taxRate)}
  Total: {calculated}
{/let}

Nested Section Edge Cases

{! Deeply nested sections !}
{#if user}
  {#if user.orders}
    {#for order in user.orders}
      {#if order.items}
        {#for item in order.items}
          {item.name}
        {/for}
      {/if}
    {/for}
  {/if}
{/if}

{! Same with null-safe navigation !}
{#for order in user.orders.orEmpty}
  {#for item in order.items.orEmpty}
    {item.name}
  {/for}
{/for}

Dynamic Template Evaluation Edge Cases

{! Eval with null template string !}
{#eval template=null /}  {! Renders nothing !}

{! Eval with empty template !}
{#eval template='' /}  {! Renders nothing !}

{! Eval with missing parameters !}
{#eval template='Hello {name}!' /}  {! name resolves from outer scope !}

{! Eval with parameter masking !}
{#let name='Outer'}
  {#eval template='Hello {name}!' name='Inner' /}  {! Outputs: Hello Inner! !}
{/let}

Include/Insert Edge Cases

{! Include non-existent template !}
{#include 'missing.html' /}  {! Throws TemplateException !}

{! Include with empty data !}
{#include 'template.html' /}  {! Has access to parent context !}

{! Isolated include without data !}
{#include 'template.html' _isolated /}  {! Template has no data access !}

{! Include with fragment from non-existent template !}
{#include 'missing.html$fragment' /}  {! Throws TemplateException !}

When/Switch Edge Cases

{! When with null value !}
{#when null}
  {#is 'test'}Never matches{/is}
  {#else}Matched{/else}  {! Renders this !}
{/when}

{! When with multiple 'is' matching same value !}
{#when value}
  {#is 'A'}First match{/is}
  {#is 'A'}Never reached{/is}  {! First match wins !}
{/when}

{! Empty when !}
{#when value}
{/when}  {! Valid but produces no output !}

Best Practices

  1. Use type-safe templates when working with Quarkus for compile-time validation
  2. Leverage the elvis operator for default values instead of verbose null checks
  3. Use fragments to create reusable template components
  4. Prefer {#let} over complex inline expressions for readability
  5. Use .orEmpty when iterating over potentially null collections
  6. Escape user input by default; only use .raw for trusted content
  7. Use {#include} with _isolated when you want complete isolation from parent context
  8. Utilize {#when} for multi-way branching instead of nested {#if} statements
  9. Use metadata in loops (e.g., item_isLast) for conditional formatting
  10. Take advantage of virtual methods like .size, .isEmpty, .contains for cleaner templates

Performance Tips

  1. Cache templates - Use Qute.enableCache() or configure Quarkus caching
  2. Prefer literals when possible - Literal expressions are optimized
  3. Use type info - Type-safe templates enable optimizations
  4. Avoid complex logic in templates - Move to Java code or template extensions
  5. Use {#include} judiciously - Each inclusion has overhead
  6. Consider async rendering for I/O-bound operations

Common Template Patterns

Master-Detail Layout

base.html:

<!DOCTYPE html>
<html>
<head>
  {#insert title}<title>Default</title>{/insert}
</head>
<body>
  <header>{#insert header}Default Header{/insert}</header>
  <main>{#insert content}Default Content{/insert}</main>
  <footer>{#insert footer}Default Footer{/insert}</footer>
</body>
</html>

page.html:

{#include 'base.html'}
  {#title}<title>My Page</title>{/title}
  {#content}
    <h1>Welcome</h1>
    <p>Page content here</p>
  {/content}
{/include}

Reusable Components

{! user-card.html !}
{@org.acme.User user}
<div class="card">
  <img src="{user.avatar}" alt="{user.name}">
  <h3>{user.name}</h3>
  <p>{user.email}</p>
</div>

{! Usage in another template !}
{#for user in users}
  {#include 'user-card.html' user=user /}
{/for}

Conditional CSS Classes

<div class="item {#if item.active}active{/if} {#if item.featured}featured{/if}">
  {item.name}
</div>

<span class="{item.status.ifTruthy('badge-success').or('badge-default')}">
  {item.status ?: 'N/A'}
</span>

Pagination

{#let
  page=currentPage
  totalPages=items.size / pageSize
}
  <nav>
    {#if page > 1}
      <a href="?page={page - 1}">Previous</a>
    {/if}

    {#for i in totalPages}
      <a href="?page={i}" class="{#if i == page}active{/if}">
        {i}
      </a>
    {/for}

    {#if page < totalPages}
      <a href="?page={page + 1}">Next</a>
    {/if}
  </nav>
{/let}

API Reference

Key Java APIs for working with Qute templates:

// Engine creation
Engine engine = Engine.builder()
    .addDefaults()
    .build();

// Parse template
Template template = engine.parse("Hello {name}!");

// Create instance with data
TemplateInstance instance = template
    .data("name", "World")
    .data("count", 42);

// Render synchronously
String output = instance.render();

// Render asynchronously
CompletionStage<String> future = instance.renderAsync();

// Get fragment
Fragment fragment = template.getFragment("fragmentId");

// Type-safe templates (Quarkus)
@CheckedTemplate
public class Templates {
    public static native TemplateInstance hello(String name);
}

// Template extension (Quarkus)
@TemplateExtension
public class MyExtensions {
    public static String upper(String str) {
        return str.toUpperCase();
    }
}

// Template global (Quarkus)
@TemplateGlobal
public class Globals {
    public static final String APP_NAME = "MyApp";

    public static String formatDate(LocalDate date) {
        return date.format(DateTimeFormatter.ISO_DATE);
    }
}

// Namespace resolver
NamespaceResolver resolver = NamespaceResolver.builder("mydata")
    .resolve(ctx -> dataService.getData(ctx.getName()))
    .build();

// Value resolver
ValueResolver resolver = ValueResolver.builder()
    .appliesTo(ctx -> ctx.getBase() instanceof MyClass)
    .resolveSync(ctx -> ((MyClass) ctx.getBase()).getValue())
    .build();

This comprehensive reference covers all major features of Qute template syntax including edge cases and common pitfalls. For Quarkus-specific features like CDI integration, message bundles, and configuration access, refer to the related documentation files.