Skip to content


Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?

Latest commit


Git stats


Failed to load latest commit information.
Latest commit message
Commit time

TINE Logo lib-freemarker

Freemarker library for Enonic XP


The most straight forward way to get it going is probably jitpack, which builds and makes a public github repo available as a gradle dependency on-the-fly:

Step 1: Add it in your root build.gradle at the end of repositories:

allprojects {
  repositories {
    maven { url '' }

Step 2. Add the dependency If you are on an Enonic XP 6.15 add the dependency like this:

dependencies {
  include 'com.github.tineikt:xp-lib-freemarker:1.1.1'

If you are on an Enonic XP 7 add the dependency like this:

dependencies {
  include 'no.tine.xp:xp-lib-freemarker:2.0.2'

Hopefully this should allow jitpack to build the project, and present it as a standard gradle package for Enonic XP :)


Just as you are used to with Thymeleaf in your controller

var freemarker = require('/lib/tineikt/freemarker');

exports.get = function(req) {
  var model = {


  var view = resolve('template.ftl');
  var html = freemarker.render(view, model);

  return {
    body: html

View Functions

Enonic View Functions documentation

Example usage in Thymeleaf:

<a data-th-href="${portal.assetUrl({'_path=css/main.css'})}">Link</a>
<img data-th-src="${portal.imageUrl({'_id=869b29a0-dccc-4d5e-afc5-81e5050a628d', '_scale=block(50,50)'})}"/>
<p data-th-text="${portal.localize({'_key=mystring','_locale=en'})}">Not translated</p>

Example usage in Freemarker

<a href="[@assetUrl path='css/main.css'/]">Link</a>
<img src="[@imageUrl scale='block(50,50)' id='869b29a0-dccc-4d5e-afc5-81e5050a628d'/]">
<p>[@localize locale='en' key='mystring'/]</p>

Formatting Dates

You can format Java 8 dates (e.g. java.time.LocalDateTime or java.time.ZonedDateTime) inside your Freemarker template.

The lib-xp-time XP-library provides access to the Java-types so that you can parse strings into java.time.* objects.

[#-- @ftlvariable name="locale" type="java.lang.String" --]
[#-- @ftlvariable name="date" type="java.time.ZonedDateTime" --]

[#setting locale=locale]

<time datetime="${date.format("ISO_OFFSET_DATE_TIME")}">

Fixing unresolved references in IntelliJ

If you are using one of the IDEs from Jetbrains, a special comment syntax exists that will fix unresolved references to Enonic View Functions in your project.

You can simply add a file "./src/main/resources/freemarker_implicit.ftl" with the following contents to your project.

[#-- @implicitly included --]

[#macro pageUrl id="" path="" type="server"][/#macro]
[#macro assetUrl path application="" type="server"][/#macro]
[#macro imageUrl scale id="" path="" format="" quality=85 background="" filter="" type="server"][/#macro]
[#macro attachmentUrl id="" path="" name="" label="source" download=false type="server"][/#macro]
[#macro componentUrl id="" path="" component="" type="server"][/#macro]
[#macro serviceUrl service application="" type="server"][/#macro]
[#macro localize key locale=""][/#macro]
[#macro processHtml value type="server"][/#macro]
[#macro imagePlaceholder width height][/#macro]

Protip: You can provide type checking to your Freemarker-templates by creating @ftlvariable comments on the top of your ftl-files.

[#-- @ftlvariable name="displayName" type="java.lang.String" --]

Freemarker documentation

What is Apache FreeMarker™?

Component reference

Enonic XP does some post-processing to retrieve and render components. You are probably used to Thymeleaf and how some magic happens when you use the data-portal-component.

<div data-th-each="component : ${regions.components}" data-th-remove="tag">
  <div data-portal-component="${component.path}" data-th-remove="tag" />

This magic is implemented using a Freemarker directive, <@component path=component.path /> The same template, in Freemarker would then be.

[#list regions.components as comp]
  [@component path=comp.path /]