This is a short guide on how to use Twig templates with Kirby CMS. It supposes that you have installed and enabled the Twig Plugin already.
A few basic examples of Twig syntax:
{# This is a comment #}
{# Output a variable as text #}
<title>{{ myTitle }}</title>
{# The dot notation in 'page.title' works for
arrays, object properties and object methods. #}
<h1>{{ page.title }}</h1>
{% if page.abstract.isNotEmpty %}
<p>{{ page.abstract }}</p>
{% endif %}
{# If the 'list' variable is an array or iterable
object, we can loop on it, yay! #}
<ul>
{% for item in list %}
<li><a href="{{ item.url }}">{{ item.title }}</li>
{% endfor %}
</ul>
Trying to use a variable that doesn’t exist will result in an error. If you’re not sure the variable will be defined, you can use the default
filter:
{# Will still result in an error if the
'description' variable does not exist: #}
{% if description %}
<meta name="description" value="{{ description }}">
{% endif %}
{# Safer test: #}
{% if description|default(0) %}
<meta name="description" value="{{ description }}">
{% endif %}
By default, Twig will escape HTML tags and entities (to help prevent cross-site scripting attacks). When that’s not ideal, you can use the raw
filter.
{# Oops, we may end up with text that looks like:
<p>This is a paragraph.</p> #}
{{ page.text.kirbytext }}
{# It’s content we trust, use it as is: #}
{{ page.text.kirbytext | raw }}
A very common pattern when using Twig is to create a “base” template that other templates will extend. For instance:
{# site/templates/base.twig #}
<!doctype html>
<html lang="{{ site.language() }}">
<head>
<title>{{ htmlTitle|default('No title') }}</title>
</head>
<body>
{% include '@snippets/navigation.twig' %}
{% block content %}
{% endblock %}
</body>
</html>
Then, in a specific template, you can extend this base template:
{# site/templates/default.twig #}
{% extends '@templates/base.twig' %}
{% set htmlTitle = page.title ~ ' - ' ~ site.title %}
{% block content %}
{{ page.text.kirbytext | raw }}
{% endblock %}
See the @snippets
and @templates
parts in our extends and includes? These are Twig namespaces, i.e. aliases for specific directories. Out of the box we have 4 namespaces:
@templates
@snippets
@plugins
@assets
They all point to the corresponding directory (generally site/snippets
, site/plugins
etc.), and follow Kirby’s configuration if you have changed those paths.
Finally, note that you can use the source()
function to output the contents of a file that is not a template, for instance if you want to inline a short script in a page:
<script>
{{ source('@assets/js/some-script.min.js') }}
</script>
To learn more about Twig, you should read Twig for Template Designers and the Twig Documentation.
The following variables are always available in templates (note that we’re using Twig notation for variables and object methods):
page
(Page object for the current page)site
(Site object)kirby
(Kirby instance)pages
(Page collection with the site’s top-level pages, same assite.children
)
For instance we could work with the site
object to retrieve all child pages of a 'blog'
page:
{# site/templates/blog.twig #}
{% set posts = site
.find('blog')
.children
.filterBy('status', 'published')
.sortBy('date', 'desc') %}
<h1>{{ page.title }}</h1>
{% if posts.count %}
<ul>
{% for post in posts %}
<li><a href="{{ post.url }}">{{ post.title }}</a></li>
{% endfor %}
</ul>
{% endif %}
You can use all of Kirby’s chaining API for $page
, $site
, etc. in your Twig templates, and often that’s all you will ever need.
You will need to do a little bit of translation between the PHP syntax in Kirby’s documentation and the equivalent Twig syntax. For example, this PHP code:
<?php echo $page->children()->first()->title(); ?>
… translates to the following Twig code:
{{ page.children.first.title }}
Almost all of Kirby’s helper functions are available in your Twig templates. This includes things like the css()
, js()
or snippet()
functions.
The only exceptions are:
- helpers related to sending emails or writing to files, such as
email
,upload
,structure
andtextfile
; - and the
ecco
andr
helpers, which are trivial to do with Twig syntax (for instance:{{ condition ? 'yes' : 'no' }}
is the same as Kirby’s<?php ecco(condition, 'yes', 'no') ?>
.
- Use the
c__get(configName, defaultValue)
function in Twig templates to get config values (shortcut forc::get
). - Use the
l__get(configName, defaultValue)
function in Twig templates to get language-specific config values or translation strings (shortcut forl::get
).
The Kirby Toolkit API is not available in Twig templates. Some methods, such as string comparisons, can be done directly with Twig syntax. Other things, like reading from a database, should probably be done in a controller instead (see the next section for more information on controllers).
Functions or classes defined by Kirby plugins will not be picked up in Twig templates. If you do want to use them in your templates, see: Exposing functions and classes to Twig templates.
You can send more data to templates by writing a Controller. Don’t worry, it’s really easy.
In the previous example, the part where we defined the posts
variable could go in a controller file:
<?php // site/controllers/blog.php
return function($site, $pages, $page) {
$data = [];
$data['posts'] = $site->find('blog')->children()
->filterBy('status', 'published')
->sortBy('date', 'desc');
return $data;
};
And in our template:
{# site/templates/blog.twig #}
<h1>{{ page.title }}</h1>
{% if posts.count %}
<ul>
{% for post in posts %}
<li><a href="{{ post.url }}">{{ post.title }}</a></li>
{% endfor %}
</ul>
{% endif %}
Ain’t that better? Well, you decide. :) I like separating the “logic” from the HTML markup, but if you just want to write a template and be done, do what you like best.