But these are just special cases of the generic forms, which are shown below. Note that for simplicity we only show the generic forms for sequence listing; simply replace " as item " with " as key , value " to get the generic form for hash listing.

Generic form 1:

<#list sequence as item>
    Part repeated for each item
<#else>
    Part executed when there are 0 items
</#list>

Where:

The else part is optional, and is only supported since FreeMarker 2.3.23. sequence : Expressions evaluates to a sequence or collection of the items we want to iterate through item : Name of the loop variable (not an expression) The various "parts" between the tags can contain arbitrary FTL (including nested list -s)

Generic form 2 (since FreeMarker 2.3.23):

<#list sequence>
    Part executed once if we have more than 0 items
    <#items as item>
        Part repeated for each item
    </#items>
    Part executed once if we have more than 0 items
<#else>
    Part executed when there are 0 items
</#list>

Where: see the "Where" section of Form 1 above (and thus the else part is optional here too).

The list directive executes the code between the list start-tag and list end-tag (the body of list from now on) for each value in the sequence (or collection) specified as its first parameter. For each such iteration the loop variable ( user in this example) will store the value of the current item.

The loop variable ( user ) only exists inside the list body. Also, macros/functions called from within the loop won't see it (as if it were a local variable).

Listing hashes is very similar, but you need to provide two variable names after the as ; one for the hash key, and another for the associated value. Assuming products is { "apple": 5, "banana": 10, "kiwi": 15 } :

Note that not all hash variables can be listed, because some of them isn't able to enumerate its keys. It's practically safe to assume though that hashes that stand for Java Map objects can be listed.

The else directive is used if when there are 0 items, you have to print something special instead of just printing nothing:

Note that the loop variable ( user ) doesn't exist between the else tag and the list end-tag, since that part is not part of the loop.

else must be literally (means, in the source code) inside the body of the list directive. That is, you can't moved it out into a macro or included template.

The items directive is used if you have to print (or do) something before the first list item, and after the last list item, as far as there's at least 1 item. A typical example:

If there are 0 items, the above won't print anything, thus you don't end up with an empty <ul></ul> .

That is, when the list directive has no as item parameter, the body of its is executed exactly once if there's at least one item, or not at all otherwise. It's the body of the mandatory nested items directive that will be run for each item, and hence it's also the items directive that defines the loop variable with as item , not list .

A list directive with items also can have an else directive:

parameter always has a nested items directive, and that an items directive always has an enclosing list which has no as item parameter. This is checked when the template is parsed, not when the template is executed. Thus, these rules apply on the FTL source code itself, so you can't move items out into a macro or included template.

A list can have multiple items directives, but only one of them will be allowed to run (as far as you don't leave and re-enter the enclosing list directive); and further attempts to call items will cause error. So multiple items can be utilized on different if - else branches for example, but not for iterating twice.

items directive can't have its own nested else directive, only the enclosing list can have

The loop variable ( user ) only exists inside the body of the items directive.

sep is used when you have to display something between each item (but not before the first item or after the last item). For example:

shorthand for <#sep>, </#sep></#list> ; the sep end-tag can be omitted if you would put it where the enclosing directive is closed anyway. In the next example, you couldn't use such abbreviation (HTML tags close nothing, as they are just raw text to output for FreeMarker):

item ?has_next>...</#if>. Thus, it can be used anywhere where there's a list or items loop variable available, it can occur for multiple times, and it can have arbitrary nested content.

The parser ensures that sep is only used on a place where there's a visible loop variable. This happens earlier than the actual execution of the template. Thus, you can't move sep from inside the associated list or items directive into a macro or included template (the parser can't know where those will be called from).

break is deprecated for most use cases, as it doesn't work well with <#sep> and item ?has_next . Instead, use sequence ?take_while( predicate ) to cut the sequence before you list it. See also examples here.

You can exit the iteration at any point with the break directive. For example:

The break directives can be placed anywhere inside list as far as it has as item parameter, otherwise it can be placed anywhere inside the items directive. However, it's strongly recommended to place it either before or after all the other things that you do inside the iteration. Otherwise it's easy to end up with unclosed elements in the output, or otherwise make the template harder to understand. Especially, avoid breaking out from the nested content of custom directives (like <#list ...>...<@foo>...<#break>...</@foo>...</#list> ), as the author of the directive may not expect that the closing tag ( </@foo> ) is never executed.

If the break is inside items , it will only exit from items , not from list . In general, break will only exit from the directive whose body is called for each item, and can only be placed inside such directive. So for example can't use break inside list 's else section, unless there's the list is nested into another break -able directive.

Using break together with sep or ?has_next is generally a bad idea, as these can't know if you will skip the rest of items with a break . To solve such situations see these examples .

Just like else and items , break must be literally inside body of the directive to break out from, and can't be moved out into a macro or included template.

You can skip the rest of the iteration body (the section until the </#list> or </#items> tag) with the continue directive, then FreeMarker will continue with the next item. For example:

The continue directives can be placed anywhere inside list as far as it has as item parameter, otherwise it can be placed anywhere inside the items directive. However, it's strongly recommended to place it before all the other things you do inside the iteration. Otherwise it's easy to end up with unclosed elements in the output, or otherwise make the template harder to understand. Especially, avoid breaking out from the nested content of custom directives (like <#list ...>...<@foo>...<#continue>...</@foo>...</#list> ), as the author of the directive may not expect that the closing tag ( </@foo> ) is never executed.

When you call continue , the sep directive will not be executed for that iteration. Using continue together with sep is generally a bad idea anyway, also ?has_next , ?counter , ?index , ?item_parity , etc. will not work as you certainly wanted if you completely skip items. To solve such situations see these examples .

Just like break , continue must be literally inside body of the directive whose iteration need to be "continued", and can't be moved out into a macro or included template.

Starting from 2.3.23, loop variable built-ins is the preferred way of accessing current state of the iteration. For example, here we use the counter and item_parity loop variable built-ins (see all of them in the Reference ):

In 2.3.22 and earlier, there were two extra loop variables to retrieve the iteration state instead (and they still exist for backward compatibility):

item _index ( deprecated by item ?index ): The index (0-based number) of the current item in the loop.

item _has_next ( deprecated by item ?has_next ): Boolean value that tells if the current item is the last in the sequence or not.

so in the above example, you could replace ${user?counter} with ${user_index +

If you need to skip certain element in a list, it's generally a bad idea to use if directive for that, because then <#sep> , item ?has_next , item ?counter , item ?index , item ?item_parity , etc., will not be usable, as FreeMarker doesn't know what items were and will be actually displayed. Instead, you should try to remove the unwanted items from the sequence that you will list, and then list it (since 2.3.29). Here are some typical examples with and without if .

In this example, you want to show the recommended products from products . Here's the wrong solution with

Template
<#-- WRONG solution! The row parity classes will be possibly messed up: -->
<#list products as product>
   <#if product.recommended>
     <div class="${product?item_parity}Row">${product.name}</div>
</#list>