or run

tessl search
Log in

Version

Workspace
tessl
Visibility
Public
Created
Last updated
Describes
mavenpkg:maven/io.quarkus/quarkus-qute@3.30.x
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

index.mddocs/

Quarkus Qute

Qute is a templating engine designed specifically for Quarkus applications that enables type-safe, build-time validated templates for web pages, emails, and other content. It minimizes reflection usage for optimal native image sizes, combines imperative and reactive programming styles, and provides comprehensive development-time features including hot reload for templates with instant change visibility.

Package Information

  • Package Name: quarkus-qute
  • Package Coordinates: io.quarkus:quarkus-qute
  • Package Type: maven
  • Language: Java
  • Installation: Add to your Maven pom.xml or Gradle build file
<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-qute</artifactId>
</dependency>

Core Imports

import io.quarkus.qute.Template;
import io.quarkus.qute.TemplateInstance;
import io.quarkus.qute.Engine;
import io.quarkus.qute.CheckedTemplate;

For CDI injection:

import jakarta.inject.Inject;
import io.quarkus.qute.Location;

For message bundles:

import io.quarkus.qute.i18n.MessageBundle;
import io.quarkus.qute.i18n.Message;
import io.quarkus.qute.i18n.Localized;
import io.quarkus.qute.i18n.MessageBundles;

Basic Usage

Simple Template Injection

import jakarta.inject.Inject;
import io.quarkus.qute.Template;
import io.quarkus.qute.TemplateInstance;

public class MyService {

    @Inject
    Template hello;  // Injects templates/hello.txt or templates/hello.html

    public String renderGreeting(String name) {
        return hello.data("name", name).render();
    }
}

Type-Safe Templates with @CheckedTemplate

import io.quarkus.qute.CheckedTemplate;
import io.quarkus.qute.TemplateInstance;

public class ItemResource {

    @CheckedTemplate
    public static class Templates {
        // Defines template at templates/ItemResource/item.html
        public static native TemplateInstance item(Item item);

        // Defines template at templates/ItemResource/items.html
        public static native TemplateInstance items(List<Item> items);
    }

    public TemplateInstance getItem(Long id) {
        Item item = findItem(id);
        return Templates.item(item);  // Type-safe template rendering
    }
}

Programmatic Template Rendering

import io.quarkus.qute.Engine;
import jakarta.inject.Inject;

public class DynamicTemplateService {

    @Inject
    Engine engine;

    public String renderDynamic() {
        Template template = engine.parse("Hello {name}!");
        return template.data("name", "World").render();
    }
}

Architecture

Qute's architecture consists of several key components:

  • Engine: Central template management with caching and configuration
  • Template: Immutable template definition created from parsed template files
  • TemplateInstance: Mutable instance with associated data for rendering
  • ValueResolver: Resolution chain for accessing and transforming data in templates
  • SectionHelper: Custom template sections (if, loop, with, include, etc.)
  • MessageBundle: Internationalization system with localized message templates
  • CheckedTemplate: Build-time type-safe template validation

Templates are located in src/main/resources/templates/ by default. In development mode, changes to template files are detected automatically and reflected immediately without restart.

Capabilities

Template Basics

Core template rendering functionality including template injection, instance creation, data binding, and synchronous/asynchronous rendering. Supports multiple data formats and template variants.

interface Template {
    TemplateInstance instance();
    TemplateInstance data(Object data);
    TemplateInstance data(String key, Object data);
    TemplateInstance data(String key1, Object data1, String key2, Object data2);
    TemplateInstance data(String key1, Object data1, String key2, Object data2, String key3, Object data3);
    TemplateInstance data(String key1, Object data1, String key2, Object data2, String key3, Object data3, String key4, Object data4);
    TemplateInstance data(String key1, Object data1, String key2, Object data2, String key3, Object data3, String key4, Object data4, String key5, Object data5);
    String render(Object data);
    String render();
    List<Expression> getExpressions();
    Expression findExpression(Predicate<Expression> predicate);
    String getGeneratedId();
    String getId();
    Optional<Variant> getVariant();
    List<ParameterDeclaration> getParameterDeclarations();
    Fragment getFragment(String id);
    Set<String> getFragmentIds();
    boolean isFragment();
    List<TemplateNode> getNodes();
    Collection<TemplateNode> findNodes(Predicate<TemplateNode> predicate);
    SectionNode getRootNode();
    Optional<URI> getSource();
}

interface TemplateInstance {
    TemplateInstance data(Object data);
    TemplateInstance data(String key, Object data);
    TemplateInstance computedData(String key, Function<String, Object> function);
    TemplateInstance setAttribute(String key, Object value);
    Object getAttribute(String key);
    String render();
    CompletionStage<String> renderAsync();
    Uni<String> createUni();
    Multi<String> createMulti();
    CompletionStage<Void> consume(Consumer<String> consumer);
    long getTimeout();
    Template getTemplate();
    Template getFragment(String id);
    TemplateInstance onRendered(Runnable action);
    TemplateInstance setLocale(String locale);
    TemplateInstance setLocale(Locale locale);
    TemplateInstance setVariant(Variant variant);
    TemplateInstance setCapacity(int capacity);
}

Template Basics

Type-Safe Templates

Type-safe template declaration and validation using @CheckedTemplate annotation. Enables compile-time validation of template parameters and expressions for enhanced reliability.

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@interface CheckedTemplate {
    String basePath() default DEFAULTED;
    boolean requireTypeSafeExpressions() default true;
    String defaultName() default ELEMENT_NAME;
    boolean ignoreFragments() default false;
}

Checked Templates

Template Syntax

Complete template expression language including value expressions, conditional sections, loops, includes, fragments, and operators. Supports nested expressions and method calls.

Template Syntax

Template Extensions

Built-in template extension methods for strings, collections, numbers, dates, and maps. Includes custom extension method creation using @TemplateExtension annotation.

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.FIELD })
@interface TemplateExtension {
    String namespace() default ANY;
    String matchName() default EMPTY;
    String[] matchNames() default {};
    int priority() default DEFAULT_PRIORITY;
}

Template Extensions

Value Resolvers

Custom value resolution for accessing and transforming data in templates. Supports property access, method invocation, and custom resolution strategies.

interface ValueResolver extends Resolver, WithPriority {
    int getPriority();
    boolean appliesTo(EvalContext context);
    CompletionStage<Object> resolve(EvalContext context);
    ValueResolver getCachedResolver(EvalContext context);
    Set<String> getSupportedProperties();
    Set<String> getSupportedMethods();
}

interface NamespaceResolver extends Resolver {
    CompletionStage<Object> resolve(EvalContext context);
    String getNamespace();
    int getPriority();
}

Value Resolvers

Message Bundles

Internationalization system with type-safe message bundles using @MessageBundle and @Message annotations. Supports localized message templates with parameter substitution.

@Retention(RUNTIME)
@Target(TYPE)
@interface MessageBundle {
    String value() default DEFAULTED_NAME;
    String defaultKey() default Message.ELEMENT_NAME;
    String locale() default DEFAULT_LOCALE;
}

@Retention(RUNTIME)
@Target(METHOD)
@interface Message {
    String key() default DEFAULT_NAME;
    String value() default DEFAULT_VALUE;
    String defaultValue() default DEFAULT_VALUE;
}

@Qualifier
@Retention(RUNTIME)
@Target({ TYPE, METHOD, FIELD, PARAMETER })
@interface Localized {
    String value();
}

class MessageBundles {
    public static <T> T get(Class<T> bundleInterface);
    public static <T> T get(Class<T> bundleInterface, Localized localized);
}

Message Bundles

Template Engine

Advanced engine configuration, template caching, custom section helpers, parser hooks, and tracing. Provides full control over template parsing and rendering behavior.

interface Engine extends ErrorInitializer {
    static EngineBuilder builder();
    Template parse(String content);
    Template parse(String content, Variant variant);
    Template parse(String content, Variant variant, String id);
    Template getTemplate(String id);
    Template putTemplate(String id, Template template);
    boolean isTemplateLoaded(String id);
    void clearTemplates();
    void removeTemplates(Predicate<String> test);
    List<ResultMapper> getResultMappers();
    String mapResult(Object result, Expression expression);
    SectionHelperFactory<?> getSectionHelperFactory(String name);
    Map<String, SectionHelperFactory<?>> getSectionHelperFactories();
    List<ValueResolver> getValueResolvers();
    List<NamespaceResolver> getNamespaceResolvers();
    Evaluator getEvaluator();
    Optional<TemplateLocation> locate(String id);
    List<TemplateInstance.Initializer> getTemplateInstanceInitializers();
    long getTimeout();
    boolean useAsyncTimeout();
    boolean removeStandaloneLines();
    TraceManager getTraceManager();
    void addTraceListener(TraceListener listener);
    void removeTraceListener(TraceListener listener);
    EngineBuilder newBuilder();
}

interface EngineBuilder {
    EngineBuilder addValueResolver(ValueResolver valueResolver);
    EngineBuilder addNamespaceResolver(NamespaceResolver namespaceResolver);
    EngineBuilder addSectionHelper(SectionHelperFactory<?> factory);
    EngineBuilder addResultMapper(ResultMapper mapper);
    EngineBuilder addLocator(TemplateLocator locator);
    EngineBuilder addParserHook(ParserHook hook);
    EngineBuilder addTemplateInstanceInitializer(TemplateInstance.Initializer initializer);
    EngineBuilder addDefaultSectionHelpers();
    EngineBuilder addDefaultValueResolvers();
    EngineBuilder addDefaults();
    EngineBuilder removeStandaloneLines(boolean value);
    EngineBuilder strictRendering(boolean value);
    EngineBuilder iterationMetadataPrefix(String prefix);
    EngineBuilder timeout(long value);
    EngineBuilder useAsyncTimeout(boolean value);
    EngineBuilder enableTracing(boolean value);
    Engine build();
}

Engine and Advanced Features

Template Globals

Declares global variables and functions accessible from all templates using @TemplateGlobal annotation. Globals can be constants or computed values available without passing them as template data.

@Retention(RUNTIME)
@Target({FIELD, METHOD})
@interface TemplateGlobal {
    String ELEMENT_NAME = "<<element name>>";
    String name() default ELEMENT_NAME;
}

Usage example:

public class Globals {
    @TemplateGlobal
    public static final String APP_NAME = "MyApp";

    @TemplateGlobal(name = "currentYear")
    public static int getCurrentYear() {
        return LocalDate.now().getYear();
    }
}

Template usage: {APP_NAME} © {currentYear}

Utility Classes

Convenience classes and helpers for template operations including the Qute static accessor, escaping utilities, raw strings, and template instance decorators.

class Qute {
    public static Engine engine();
    public static void setEngine(Engine engine);
    public static String fmt(String template, Map<String, Object> data);
    public static String fmt(String template, Object... data);
    public static Fmt fmt(String template);
    public static void enableCache();
    public static void disableCache();
    public static void clearCache();
}

class Escaper {
    public String escape(CharSequence value);
    public static Builder builder();
}

class RawString {
    public RawString(String value);
    public String getValue();
}

class HtmlEscaper extends CharReplacementResultMapper { }
class JsonEscaper implements ResultMapper { }

abstract class ForwardingTemplateInstance implements TemplateInstance {
    protected abstract TemplateInstance delegate();
}

class ResultsCollectingTemplateInstance extends ForwardingTemplateInstance { }

interface TemplateGlobalProvider extends TemplateInstance.Initializer, NamespaceResolver { }

Utility Classes

Configuration

Qute configuration options including template suffixes, content types, escaping, charset, and type checking. Configured via application.properties using quarkus.qute.* prefix.

@ConfigRoot(phase = ConfigPhase.BUILD_AND_RUN_TIME_FIXED)
@ConfigMapping(prefix = "quarkus.qute")
interface QuteConfig {
    List<String> suffixes();
    Map<String, String> contentTypes();
    Optional<List<String>> typeCheckExcludes();
    Pattern templatePathExclude();
    String iterationMetadataPrefix();
    List<String> escapeContentTypes();
    Charset defaultCharset();
    DuplicitTemplatesStrategy duplicitTemplatesStrategy();
}

@ConfigRoot(phase = ConfigPhase.RUN_TIME)
@ConfigMapping(prefix = "quarkus.qute")
interface QuteRuntimeConfig {
    Optional<PropertyNotFoundStrategy> propertyNotFoundStrategy();
    boolean removeStandaloneLines();
    boolean strictRendering();
    long timeout();
    boolean useAsyncTimeout();
}

Configuration

Types

Core Types

class Variant {
    // Constants for common content types
    public static final String TEXT_HTML = "text/html";
    public static final String TEXT_PLAIN = "text/plain";
    public static final String TEXT_XML = "text/xml";
    public static final String APPLICATION_JSON = "application/json";

    // Factory method
    public static Variant forContentType(String contentType);

    // Constructors
    public Variant(Locale locale, Charset encoding, String contentType);
    public Variant(Locale locale, String contentType, String encoding);

    // Accessors
    public Locale getLocale();
    public String getMediaType();
    public String getContentType();
    public String getEncoding();
    public Charset getCharset();
}

interface Expression {
    // Namespace access
    String getNamespace();
    boolean hasNamespace();

    // Parts access
    List<Part> getParts();

    // Literal handling
    boolean isLiteral();
    Object getLiteral();
    CompletableFuture<Object> getLiteralValue();
    CompletionStage<Object> asLiteral();

    // Origin and metadata
    Origin getOrigin();
    String toOriginalString();
    int getGeneratedId();

    // Type information
    boolean hasTypeInfo();
    String collectTypeInfo();

    /**
     * Part of an expression (property or virtual method).
     */
    interface Part {
        String getName();
        String getTypeInfo();
        boolean isVirtualMethod();
        VirtualMethodPart asVirtualMethod();
    }

    /**
     * Part that represents a virtual method with parameters.
     */
    interface VirtualMethodPart extends Part {
        List<Expression> getParameters();
    }
}

interface ErrorCode {
    String getName();
}

class TemplateException extends RuntimeException {
    // Factory method
    public static Builder builder();

    // Constructors
    public TemplateException(String message);
    public TemplateException(Throwable cause);
    public TemplateException(Origin origin, String message);
    public TemplateException(ErrorCode code, Origin origin, String messageTemplate,
                             Map<String, Object> arguments, Throwable cause);

    // Accessors
    public Origin getOrigin();
    public ErrorCode getCode();
    public Map<String, Object> getArguments();
    public String getMessageTemplate();
    public Optional<String> getCodeName();

    /**
     * Builder for TemplateException with template-based messages.
     */
    public static class Builder {
        public Builder message(String message);
        public Builder cause(Throwable cause);
        public Builder origin(Origin origin);
        public Builder code(ErrorCode code);
        public Builder argument(String key, Object value);
        public Builder arguments(Map<String, Object> arguments);
        public Builder arguments(Object... arguments);
        public TemplateException build();
    }
}

Parameter Declaration

class ParameterDeclaration {
    public String getKey();
    public String getTypeInfo();
    public Expression getDefaultValue();
    public Origin getOrigin();
}

Template Location

interface TemplateLocator extends WithPriority {
    Optional<TemplateLocation> locate(String id);
    int getPriority();

    interface TemplateLocation {
        Reader read();
        Optional<Variant> getVariant();
        Optional<URI> getSource();
    }
}

Common Patterns and Best Practices

Error Handling

When working with templates, always handle potential errors:

try {
    String result = template.data("user", user).render();
    return result;
} catch (TemplateException e) {
    // Log error with origin information
    logger.error("Template error at {}:{} - {}",
        e.getOrigin().getTemplateId(),
        e.getOrigin().getLine(),
        e.getMessage());
    // Return fallback or rethrow
    return fallbackContent;
}

Null Safety

Use elvis operator and orEmpty for null-safe templates:

// In template: {user.name ?: 'Guest'}
// In template: {#for item in items.orEmpty}{item}{/for}

// In Java: provide defaults
template.data("user", user != null ? user : new GuestUser())
       .data("items", items != null ? items : Collections.emptyList())
       .render();

Performance Optimization

  • Cache templates: Reuse Template instances, don't parse repeatedly
  • Use async rendering: For I/O-bound operations, use renderAsync() or createUni()
  • Minimize data: Only pass data that templates actually need
  • Use type-safe templates: Enable build-time optimizations
// Good: Reuse template instance
@Inject
Template emailTemplate;

public String sendEmail(User user) {
    return emailTemplate.data("user", user).render();
}

// Bad: Parse every time
public String sendEmail(User user) {
    Template template = engine.parse(emailContent);  // Expensive!
    return template.data("user", user).render();
}

Thread Safety

  • Template: Thread-safe, can be shared
  • TemplateInstance: NOT thread-safe, create new instance per rendering
  • Engine: Thread-safe, should be singleton
// Good: Share Template, create new TemplateInstance
@Inject
Template shared;  // Thread-safe

public String render(String name) {
    return shared.instance()  // New instance per call
                 .data("name", name)
                 .render();
}

Troubleshooting

Template Not Found

Error: TemplateException: Template not found: mytemplate

Solutions:

  • Verify template exists in src/main/resources/templates/
  • Check file extension matches configured suffixes
  • Ensure template name matches injection field name or @Location value
  • Check for typos in template path

Property Not Found

Error: TemplateException: Property "xyz" not found on base object

Solutions:

  • Verify object has getter method or public field
  • Check for typos in property name
  • Use orEmpty or elvis operator for optional properties
  • Add @TemplateData annotation to expose properties
  • Check quarkus.qute.type-check-excludes configuration

Type Mismatch

Error: Build fails with type checking errors

Solutions:

  • Add parameter declarations: {@org.acme.User user}
  • Use @CheckedTemplate for type-safe templates
  • Verify method parameters match template parameters
  • Check generic types are correctly specified

Timeout Errors

Error: TemplateException: Rendering timeout

Solutions:

  • Increase timeout: quarkus.qute.timeout=30000
  • Use async rendering for long operations
  • Optimize value resolvers (avoid slow operations)
  • Check for infinite loops in template logic

Escaping Issues

Problem: HTML tags appear as text instead of rendering

Solutions:

  • Use RawString for trusted HTML: new RawString("<b>Bold</b>")
  • Use .raw virtual method in template: {content.raw}
  • Verify content type is set correctly
  • Check quarkus.qute.escape-content-types configuration

Edge Cases and Special Scenarios

Null Value Handling

Templates handle null values gracefully with proper operators:

// Template with null-safe access
template.data("user", null).render();  // Use {user ?: 'Guest'} in template

// Optional values
template.data("value", Optional.empty()).render();  // Use {value ?: 'default'}

// Null in collections
List<String> listWithNulls = Arrays.asList("a", null, "b");
template.data("items", listWithNulls).render();  // Nulls are preserved

Empty Collections

// Empty list - use orEmpty in template
template.data("items", Collections.emptyList()).render();
// Template: {#for item in items.orEmpty}...{#else}No items{/for}

// Null list
template.data("items", null).render();
// Template: {#for item in items.orEmpty}...{#else}No items{/for}

Async Data Resolution

When data requires async loading, use computed data:

template.instance()
    .data("userId", userId)
    .computedData("user", key -> userService.findByIdAsync(userId))
    .renderAsync()
    .thenAccept(result -> sendResponse(result));

Fragment Rendering

Render specific fragments for partial updates:

Template page = engine.getTemplate("page.html");

// Render full page
String fullPage = page.data("user", user).render();

// Render just the header fragment
Template.Fragment header = page.getFragment("header");
String headerHtml = header.data("user", user).render();

// Check available fragments
Set<String> fragmentIds = page.getFragmentIds();

Timeout Handling

try {
    String result = template.instance()
        .data("data", data)
        .setAttribute(TemplateInstance.TIMEOUT, 5000L)  // 5 second timeout
        .render();
} catch (TemplateException e) {
    if (e.getCode() != null && "TIMEOUT".equals(e.getCode().getName())) {
        // Handle timeout specifically
        logger.warn("Template rendering timed out");
        return fallbackContent;
    }
    throw e;
}

Locale-Specific Rendering

// Set locale for message bundles and formatting
template.instance()
    .setLocale(Locale.forLanguageTag("de-DE"))
    .data("user", user)
    .render();

// Or via attribute
template.instance()
    .setAttribute(TemplateInstance.LOCALE, Locale.FRENCH)
    .data("user", user)
    .render();

Custom Capacity for Large Output

// Pre-allocate StringBuilder capacity for large templates
template.instance()
    .setCapacity(10000)  // 10KB initial capacity
    .data("items", largeList)
    .render();

Performance Considerations

Template Caching

Templates are automatically cached by the Engine. Reuse Template instances:

// Good: Inject once, reuse many times
@Inject
Template emailTemplate;

public void sendEmails(List<User> users) {
    for (User user : users) {
        String email = emailTemplate.data("user", user).render();
        send(email);
    }
}

// Bad: Parse repeatedly
public void sendEmails(List<User> users) {
    for (User user : users) {
        Template template = engine.parse(emailContent);  // Expensive!
        String email = template.data("user", user).render();
        send(email);
    }
}

Async Rendering for I/O

Use async rendering when templates involve I/O operations:

// Async rendering with Mutiny
Uni<String> result = template.instance()
    .data("userId", userId)
    .computedData("user", key -> userService.loadAsync(userId))
    .createUni();

// Async rendering with CompletionStage
CompletionStage<String> result = template.instance()
    .data("data", data)
    .renderAsync();

Streaming Large Output

For very large output, stream chunks instead of building complete string:

template.instance()
    .data("largeDataset", dataset)
    .consume(chunk -> outputStream.write(chunk.getBytes()))
    .thenRun(() -> outputStream.close());