Nette uses the Latte template system. Latte is used because it is the most secure template system for PHP, and at the same time the most intuitive system. You don't have to learn much new, you just need to know PHP and a few Latte tags.

It is usual that the page is completed from the layout template + the action template. This is what a layout template might look like, notice the blocks {block} and tag {include}:

<!DOCTYPE html>
	<title>{block title}My App{/block}</title>
	{include content}

And this might be the action template:

{block title}Homepage{/block}

{block content}

It defines block content, which is inserted in place of {include content} in the layout, and also re-defines block title, which overwrites {block title} in the layout. Try to imagine the result.

Search for Templates

The path to the templates is deduced according to simple logic. It tries to see if one of these template files exists relative to the directory where presenter class is located, where <Presenter> is the name of the current presenter and <view> is the name of the current action:

  • templates/<Presenter>/<view>.latte
  • templates/<Presenter>.<view>.latte

If it does not find the template, the response is error 404.

You can also change the view using $this->setView('otherView'). Or, instead of searching, directly specify the name of the template file using $this->template->setFile('/path/to/template.latte').

You can change the paths where templates are searched by overriding the formatTemplateFiles method, which returns an array of possible file paths.

The layout is expected in the following files:

  • templates/<Presenter>/@<layout>.latte
  • templates/<Presenter>.@<layout>.latte
  • templates/@<layout>.latte layout common to multiple presenters

<Presenter> is the name of the current presenter and <layout> is the name of the layout, which is by default 'layout'. The name can be changed with $this->setLayout('otherLayout'), so that @otherLayout.latte files will be tried.

You can also directly specify the file name of the layout template using $this->setLayout('/path/to/template.latte'). Using $this->setLayout(false) will disable the layout searching.

You can change the paths where templates are searched by overriding the formatLayoutTemplateFiles method, which returns an array of possible file paths.

Variables in the Template

Variables are passed to the template by writing them to $this->template and then they are available in the template as local variables:

$this->template->article = $this->articles->getById($id);

In this way we can easily pass any variables to the templates. However, when developing robust applications, it is often more useful to limit ourselves. For example, by explicitly defining a list of variables that the template expects and their types. This will allow PHP to type-check, the IDE to autocomplete correctly, and static analysis to detect errors. This is possible since nette/application version 3.1.

And how do we define such an enumeration? Simply in the form of a class and its properties. We name it similarly to presenter, but with Template at the end:

 * @property-read ArticleTemplate $template
class ArticlePresenter extends Nette\Application\UI\Presenter

class ArticleTemplate extends Nette\Bridges\ApplicationLatte\Template
	/** @var Model\Article */
	public $article;

	/** @var Nette\Security\User */
	public $user;

	// and other variables

The $this->template object in the presenter will now be an instance of the ArticleTemplate class. So PHP will check the declared types when they are written. And starting with PHP 8.2 it will also warn about writing to a non-existent variable, in previous versions the same can be achieved using the Nette\SmartObject trait.

The @property-read annotation is for IDE and static analysis, it will make autocomplete work, see PhpStorm and code completion for $this->template.

You can indulge in the luxury of whispering in templates too, just install the Latte plugin in PhpStorm and specify the class name at the beginning of the template, see the article Latte: how to type system:

{templateType App\Presenters\ArticleTemplate}

This is also how templates work in components, just follow the naming convention and create a template class FifteenTemplate for the component e.g. FifteenControl.

If you need to create a $template as an instance of another class, use the createTemplate() method:

public function renderDefault(): void
	$template = $this->createTemplate(SpecialTemplate::class);
	$template->foo = 123;
	// ...

Default Variables

Presenters and components pass several useful variables to templates automatically:

  • $basePath is an absolute URL path to root dir (for example /CD-collection)
  • $baseUrl is an absolute URL to root dir (for example http://localhost/CD-collection)
  • $user is an object representing the user
  • $presenter is the current presenter
  • $control is the current component or presenter
  • $flashes list of messages sent by method flashMessage()

If you use a custom template class, these variables are passed if you create a property for them.

In template we create links to other presenters & actions as follows:

<a n:href="Product:show">detail</a>

Attribute n:href is very handy for HTML tags <a>. If we want to print the link elsewhere, for example in the text, we use {link}:

URL is: {link Home:default}

For more information, see Creating Links.

Custom Filters, Tags, etc.

The Latte templating system can be extended with custom filters, functions, tags, etc. This can be done directly in the render<View> or beforeRender() method:

public function beforeRender(): void
	// adding a filter
	$this->template->addFilter('foo', /* ... */);

	// or configure the Latte\Engine object directly
	$latte = $this->template->getLatte();
	$latte->addFilterLoader(/* ... */);

Latte version 3 offers a more advanced way by creating an extension for each web project. Here is a rough example of such a class:

namespace App\Templating;

final class LatteExtension extends Latte\Extension
	public function __construct(
		private App\Model\Facade $facade,
		private Nette\Security\User $user,
		// ...
	) {

	public function getFilters(): array
		return [
			'timeAgoInWords' => $this->filterTimeAgoInWords(...),
			'money' => $this->filterMoney(...),
			// ...

	public function getFunctions(): array
		return [
			'canEditArticle' =>
				fn($article) => $this->facade->canEditArticle($article, $this->user->getId()),
			// ...

	// ...

We register it using configuration:

		- App\Templating\LatteExtension