Latte is a template engine for PHP which eases your work and ensures the output is protected against vulnerabilities, such as XSS.
  • Latte is fast: it compiles templates to plain optimized PHP code.
  • Latte is secure: it is the first PHP engine introducing context-aware escaping and link checking.
  • Latte speaks your language: it has intuitive syntax and helps you to build better websites easily.

Although PHP is originally a templating language, it is not particularly suited for writing templates. Let's have a look at an example of a PHP template that prints an array $items as a list:

<?php if ($items): ?>
    <?php $counter = 1 ?>
    <?php foreach ($items as $item): ?>
        <li id="item-<?php echo $counter++ ?>"><?php
        echo htmlSpecialChars(mb_convert_case($item, MB_CASE_TITLE)) ?>
    <?php endforeach ?>
<?php endif?>

The code is rather confusing. Moreover, we must not forget to call htmlSpecialChars function. That's why there are so many different template engines for PHP. One of the best template engines is part of Nette Framework and it is called Latte. You'll love it!

The same template as the one above can be written easily in Latte:

<ul n:if="$items">
{foreach $items as $item}
    <li id="item-{$iterator->counter}">{$item|capitalize}</li>

As you can see there are two types of directives:

  • tags in braces, for example {foreach …}
  • n:attributes, for example n:if="…"

Installation and Usage

The best way how to install Latte is to download a latest package or use a Composer:

composer require latte/latte

The Latte requires PHP version 5.4.4 and supports PHP up to 7.3.

How to render template? Just run this code:

$latte = new Latte\Engine;


$parameters = [
    'items' => ['one', 'two', 'three'],

// render to output
$latte->render('template.latte', $parameters);
// or render to string
$html = $latte->renderToString('template.latte', $parameters);

Latte automatically regenerates the cache every time you change the template, which can be turned off in the production environment to save a little performance:



You can find detailed description of all the built-in tags. Furthermore, you can make your own tags.

Each pair tag, such as {if} … {/if}, operating upon single HTML element can be written in n:attribute notation. So, it is possible to write the {foreach} tag in the same manner:

<ul n:if="$items">
    <li n:foreach="$items as $item">{$item|capitalize}</li>

With n:attributes you can do much more interesting tricks as you will see in a moment.

{$item|capitalize} tag which prints the $item variable contains so called filter, in this case the capitalize filter which makes the first letter of each word uppercase.

Very important feature of Latte is that it escapes variables by default. Escaping is needed when printing a variable because we have to convert all the characters which have a special meaning in HTML to other sequences. In case we forget it can lead to a serious security hole called Cross Site Scripting (XSS).

Because of different escaping functions that are needed in different documents and different parts of a page, Latte features a unique technology of Context-Aware Escaping which recognizes the context in which the tag is placed and chooses the right escaping mode. You don't have to worry that your coder forgets about it causing you goose bumps because of a security hole. Which is great!

If the $item variable stores an HTML code and you want to print it without any alteration you just add the modifier noescape: {$item|noescape}. Forgetting the modifier mark won't cause any security holes in spirit of „less code, more security“ principle.

You can still use PHP inside the tags normally, including comments as well. But Latte also extends the PHP syntax with three pleasant features:

  1. array can be written as [1, 2, 3], which is the same as [1, 2, 3] in PHP
  2. we can omit quotes around the strings consisting of letters, numbers and dashes
  3. short condition notation $a ? 'b' which is the same as $a ? 'b' : null in PHP

For example:

{foreach [a, b, c] as $id} ... {/foreach}

{$cond ? hello}  // prints 'hello' if $cond equals true

Latte also has a {* comment tag *} which doesn't get printed to the output.


We showed that n:attributes are supposed to be written directly into HTML tags as their special attributes. We also said that every pair tag (e.g. {if} … {/if}) can be written in n:attribute notation. The functionality then corresponds to the HTML element in which it is written:

{var $items = ['I', '♥', 'Nette Framework']}

<p n:foreach="$items as $item">{$item}</p>


<p>Nette Framework</p>

By using inner- prefix we can alter the behavior so that the functionality applies only to the body of the element:

<div n:inner-foreach="$items as $item">


    <p>Nette Framework</p>

Or by using tag- prefix the functionality is applied on the HTML tags only:

<p><a href="{$url}" n:tag-if="$url">Title</a></p>

Depending on the value of $url variable this will print:

// when $url is empty

// when $url equals ''
<p><a href="">Title</a></p>

However, n:attributes are not only a shortcut for pair tags, there are some pure n:attributes as well, for example the coder's best friend n:class.


See the summary of standard filters.

Latte allows calling filters by using the pipe sign notation called modifier (preceding space is allowed):


Filters can be chained, in that case they apply in order from left to right:


Parameters are put after the filter name separated by colon or comma:


Filters can be applied on expression:

{var $name = ($title|upper) . ($subtitle|lower)}</h1>

Custom filters

Custom filters can be registered this way:

$latte = new Latte\Engine;
$latte->addFilter('shortify', function ($s) {
    return mb_substr($s, 0, 10); // shortens the text to 10 characters

In this case it would be better for the filter to get an extra parameter:

$latte->addFilter('shortify', function ($s, $len = 10) {
    return mb_substr($s, 0, $len);

We use it in a template like this:


Universal filter

Manual registration of multiple filter can be replaced with a single universal registration:

$latte->addFilter(null, 'Filters::common');

Filters:common gets name of filter as first parameter.

class Filters
    public static function common($filter, $value)
        if (method_exists(__CLASS__, $filter)) {
            $args = func_get_args();
            return call_user_func_array([__CLASS__, $filter], $args);

    public static function shortify($s, $len = 10)
        return mb_substr($s, 0, $len);


Latte is fast. It compiles the templates to native PHP code and stores them in cache on the disk. So they are as fast as if they would have been written in pure PHP.

The template is automatically recompiled each time we change the source file. While developing you just need to edit the templates in Latte and changes are visible in your browser instantly.


With each error or typo you will be informed by the Tracy with all the luxury. The template source code is displayed and the red line marks the error showing error message as well. With just a single click you can open the template in your favorite editor and fix the error at once. Easy peasy!

If you are using an IDE with code stepping you can go through the generated PHP code of the template.


Latte syntax wasn't invented by engineers but came up from webdesigner's practical requests. We were looking for the friendliest syntax with which you can write even the most problematic constructions comfortably enough. You will be surprised how much help Latte can be.

You can find tags for advanced layout managing, for template inheritance, nested blocks and so on. Syntax comes from PHP itself so you don't have to learn anything new and you can leverage your know-how.

Context-Aware Escaping

Although the Cross Site Scripting (XSS) is one of the trivial ways of exploiting a web page it is the most common vulnerability but very serious. It can lead to identity theft and so on. The best defense is consistent escaping of printed data, ie. converting the characters which have a special meaning in the given context.

If the coder omits the escaping a security hole is made. That's why template engines implement automated escaping. The problem is that the web page has different contexts and each has different rules for escaping printed data. A security hole then shows up if the wrong escaping functions are used.

But Latte is sophisticated. It features unique technology of Context-Aware Escaping which recognizes the context in which the tag is placed and chooses the right escaping mode. What does that mean?

Latte doesn't need any manual work. All is done automatically, consistently and correctly. You don't have to worry about security holes.

Lets see how it works:

<p onclick="alert({$movie})">{$movie}</p>

<script>var movie = {$movie};</script>

If $movie variable stores 'Amarcord & 8 1/2' string it generates the following output. Notice different escaping used in HTML and JavaScript and also in onclick attribute:

<p onclick="alert(&quot;Amarcord &amp; 8 1\/2&quot;)">Amarcord &amp; 8 1/2</p>

<script>var movie = "Amarcord & 8 1\/2";</script>

Thanks to Context-Aware Escaping the template is simple and your application perfectly secured against Cross Site Scripting. You can use PHP variables natively inside the JavaScript!


Strings in JavaScript are escaped including quotes. If you want to put variable into another string, simply concatenate them:

    alert('Hello ' + {$name} + '!');  # good
    alert('Hello {$name} !');  # bad!

Latte automatically checks whether the variable used in the src or href attributes contains a web URL (ie protocol HTTP) and prevents the writing of links that may pose a security risk.

{var $link = 'javascript:attack()'}

<a href="{$link}">click here</a>


<a href="">click here</a>

The check can be turned off using a filter nocheck.

A pretty output

Sticklers will enjoy the look of the HTML output which Latte generates. All tags are indented as they are supposed to. The code looks like it has been processed with some kind of HTML code beautifier :-)

User-defined tags

Latte provides API for making your own tags. It isn't difficult at all. Tags are added in sets (a set can consist of a single tag).

$latte = new Latte\Engine;

// lets create a set
$set = new Latte\Macros\MacroSet($latte->getCompiler());

// add new pair tag {try} ... {/try}
    'try', // tag name
    'try {',  // PHP code replacing the opening brace
    '} catch (\Exception $e) {}' // code replacing the closing brace

If we omit the last parameter of addMacro() method, we denote the tag is not paired.

PHP code in the second and third parameter can contain tags:

  • %node.word – inserts the first tag argument
  • %node.array – inserts the tag arguments formated as a PHP array
  • %node.args – inserts the tag arguments formated as PHP code
  • %escape(...) – replaced by the current escape function
  • %modify(...) – replaced by a sequence of modifiers

For example:

$set->addMacro('if', 'if (%node.args):', 'endif');

If the tag logic is more complex we can use callbacks or lambda functions instead of strings. In the first parameter they will get MacroNode object which represents the current node, the second parameter is PhpWriter object which helps with generating the output code.

$set->addMacro('if', function ($node, $writer) {
    return $writer->write('if (%node.args):');
}, 'endif');

Template from string

You can load template from strings using Latte\Loaders\StringLoader:

$latte->setLoader(new Latte\Loaders\StringLoader([
    'main' => '{if true} {$var} {/if}',

$latte->render('main', $parameters);