CtrlK
BlogDocsLog inGet started
Tessl Logo

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

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

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

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());
Workspace
tessl
Visibility
Public
Created
Last updated
Describes
mavenpkg:maven/io.quarkus/quarkus-qute@3.30.x
Publish Source
CLI
Badge
tessl/maven-io-quarkus--quarkus-qute badge