Tags

The following tags are available to Twig templates in Craft:

Tag Description
apply Applies Twig filters to the nested template code.
autoescape Controls the escaping strategy for the nested template code.
block Defines a template block.
cache Caches a portion of your template.
css Registers a <style> tag on the page.
dd Dump and die.
deprecated Triggers a PHP deprecation error.
do Does.
embed Embeds another template.
exit Ends the request.
extends Extends another template.
for Loops through an array.
from Imports macros from a template.
header Sets an HTTP header on the response.
hook Invokes a template hook.
if Conditionally executes the nested template code.
import Imports macros from a template.
include Includes another template.
js Registers a <script> tag on the page.
macro Defines a macro.
namespace Namespaces input names and other HTML attributes.
nav Creates a hierarchical nav menu.
paginate Paginates an element query.
redirect Redirects the browser.
requireGuest Requires that no user is logged-in.
requireLogin Requires that a user is logged-in.
requirePermission Requires that a user is logged-in with a given permission.
set Sets a variable.
switch Switch the template output based on a give value.
use Inherits from another template horizontally.
verbatim Disables parsing of nested Twig code.
with Creates a nested template scope.

# cache

This tag will cache a portion of your template, which can improve performance for subsequent requests, as they will have less work to do.

{% cache %}
    {% for block in entry.myMatrixField.all() %}
        <p>{{ block.text }}</p>
    {% endfor %}
{% endcache %}

The cache tag is for caching output, not logic. Take care not to cache {{ csrfInput() }}, form fields, or parts of templates where dynamic output is expected.

Warning: If you’re suffering from abnormal page load times, you may be experiencing a suboptimal hosting environment. Please consult a specialist before trying {% cache %}. {% cache %} is not a substitute for fast database connections, efficient templates, or moderate query counts. Possible side effects include stale content, excessively long-running background tasks, stuck tasks, and in rare cases, death. Ask your hosting provider if {% cache %} is right for you.

# Parameters

The {% cache %} tag supports the following parameters:

# globally

Caches the output globally (for the current site locale), rather than on a per-URL basis.

{% cache globally %}

# using key

Specifies the name of the key the cache should use. If this is not provided, a random key will be generated when Twig first parses the template.

{% cache using key "page-header" %}

TIP

You can combine this parameter with globally to cache templates on a per-page basis, without letting any query string variables get included in the path:

{% cache globally using key craft.app.request.pathInfo %}

WARNING

If you change the template code within a {% cache %} that uses a custom key, any existing template caches will not automatically be purged. You will either need to assign the tag a new key, or clear your existing template caches manually using the Clear Caches tool in Settings.

# for

The amount of time it should take for the cache to expire.

{% cache for 3 weeks %}

The accepted duration units are:

  • sec(s)
  • second(s)
  • min(s)
  • minute(s)
  • hour(s)
  • day(s)
  • fortnight(s)
  • forthnight(s)
  • month(s)
  • year(s)
  • week(s)

Tip: If this parameter is omitted, your cacheDuration config setting will be used to define the default duration.

# until

A DateTime object defining when the cache should expire.

{% cache until entry.eventDate %}

TIP

You can only use for or until in a single {% cache %} tag.

# if

Only activates the {% cache %} tag if a certain condition is met.

{## Only cache if this is a mobile browser #}
{% cache if craft.app.request.isMobileBrowser() %}

# unless

Prevents the {% cache %} tag from activating if a certain condition is met.

{## Don't cache if someone is logged in #}
{% cache unless currentUser %}

TIP

You can only use if or unless in a single {% cache %} tag.

# Cache clearing

Your caches will automatically clear when any elements (entries, assets, etc.) within the tags are saved or deleted.

If you have any element queries within the tags (e.g. a craft.entries), and you create a new element that should be returned by one of the queries, Craft will also be able to figure that out and clear the cache.

You can also manually clear all of your template caches from the Settings page, using the “Clear Caches” tool.

# When to use {% cache %} tags

You should use {% cache %} tags any time you’ve got a template that’s causing a lot of database queries, or you’re doing something very computationally expensive with Twig.

Here are some examples of when to use them:

  • A big list of entries
  • A Matrix field loop, where some of the blocks have relational fields on them, adding their own additional database queries to the page
  • Whenever you’re pulling in data from another site

There are also some cases where it’s not a good idea to use them:

  • Don’t use them to cache static text; that will be more expensive than simply outputting the text.

  • You can’t use them outside of top-level {% block %} tags within a template that extends another.

  • The {% cache %} tag will only cache HTML, so using tags like {% css %} and {% js %} inside of it doesn’t make sense because they don’t actually output HTML therefore their output won’t be cached.

    {## Bad: #}
    
    {% extends "_layout" %}
    {% cache %}
        {% block "content" %}
            ...
        {% endblock %}
    {% endcache %}
    
    {## Good: #}
    
    {% extends "_layout" %}
    {% block "content" %}
        {% cache %}
            ...
        {% endcache %}
    {% endblock %}
    

Tip: The {% cache %} tag will detect if there are any ungenerated image transform URLs within it. If there are, it will hold off on caching the template until the next request, so those temporary image URLs won’t get cached.

# css

The {% css %} tag can be used to register a <style> tag in the page’s <head>.

{% css %}
    .content {
        color: {{ entry.textColor }};
    }
{% endcss %}

TIP

The tag calls yii\web\View::registerCss() under the hood, which can also be accessed via the global view variable.

{% set styles = ".content { color: #{entry.textColor}; }" %}
{% do view.registerCss(styles) %}

# Parameters

The {% css %} tag supports the following parameters:

# with

Any HTML attributes that should be included on the <style> tag.

{% css with {type: 'text/css'} %}

Attributes will be rendered by yii\helpers\BaseHtml::renderTagAttributes().

# dd

This tag will dump a variable out to the browser and then end the request. (dd stands for “Dump-and-Die”.)

{% set entry = craft.entries.id(entryId).one() %}
{% dd entry %}

# exit

This tag will prevent the rest of the template from executing, and end the request.

{% set entry = craft.entries.id(entryId).one() %}

{% if not entry %}
    {% exit 404 %}
{% endif %}

# Parameters

The {% exit %} tag supports the following parameter:

# Status

You can optionally set the HTTP status code that should be included with the response. If you do, Craft will look for the appropriate error template to render. For example, {% exit 404 %} will get Craft to return the 404.twig template. If the template doesn’t exist. Craft will fallback on its own template corresponding to the status code.

This tag will set a new HTTP header on the response.

{## Tell the browser to cache this page for 30 days #}
{% set expiry = now|date_modify('+30 days') %}

{% header "Cache-Control: max-age=" ~ (expiry.timestamp - now.timestamp) %}
{% header "Pragma: cache" %}
{% header "Expires: " ~ expiry|date('D, d M Y H:i:s', 'GMT') ~ " GMT" %}

# Parameters

The {% header %} tag supports the following parameter:

# Header

You specify the actual header that should be set by typing it as a string after the word header. This parameter is required.

# hook

This tag gives plugins and modules an opportunity to hook into the template, to either return additional HTML or make changes to the available template variables.

{## Give plugins a chance to make changes here #}
{% hook 'my-custom-hook-name' %}

See Template Hooks for details on plugins and modules can work with {% hook %} tags.

# js

The {% js %} tag can be used to register a <script> tag on the page.

{% js %}
    _gaq.push([
        "_trackEvent",
        "Search",
        "{{ searchTerm|e('js') }}"
    ]);
{% endjs %}

TIP

The tag calls yii\web\View::registerJs() under the hood, which can also be accessed via the global view variable.

{% set script = '_gaq.push(["_trackEvent", "Search", "'~searchTerm|e('js')~'"' %}
{% do view.registerJs(script) %}

# Parameters

The {% js %} tag supports the following parameters:

# Position

You can specify where the <script> tag should be added to the page using one of these position keywords:

Keyword Description
at head In the page’s <head>
at beginBody At the beginning of the page’s <body>
at endBody At the end of the page’s <body>
on load At the end of the page’s <body>, within jQuery(window).load()
on ready At the end of the page’s <body>, within jQuery(document).ready()
{% js at head %}

By default, at endBody will be used.

WARNING

Setting the position to on load or on ready will cause Craft to load its internal copy of jQuery onto the page (even if the template is already including its own copy), so you should probably avoid using them in front-end templates.

# namespace

The {% namespace %} tag can be used to namespace input names and other HTML attributes.

For example, this:

{% namespace 'foo' %}
<input id="title" name="title" type="text">
{% endnamespace %}

would become this:

<input id="foo-title" name="foo[title]" type="text">

Notice how the id attribute changed from title to foo-title, and the name attribute changed from title to foo[title].

TIP

This tag works identically to the namespace filter, except that it will call craft\web\View::setNamespace() at the beginning, so any PHP code executed within it can be aware of what the nested IDs will become.

This tag helps create a hierarchical navigation menu for entries in a Structure section or a Category Group.

{% set entries = craft.entries.section('pages').all() %}

<ul id="nav">
    {% nav entry in entries %}
        <li>
            <a href="{{ entry.url }}">{{ entry.title }}</a>
            {% ifchildren %}
                <ul>
                    {% children %}
                </ul>
            {% endifchildren %}
        </li>
    {% endnav %}
</ul>

# Parameters

The {% nav %} tag has the following parameters:

# Item name

The first thing to follow “{% nav” is the variable name you’d like to use to represent each item in the loop, e.g. item, entry, or category. You will be using this variable name to reference the items inside the loop.

# in

Next you need to type the word “in”, followed by the array of entries the tag should loop through. This can be an array of elements, or an element query.

WARNING

The {% nav %} tag requires elements to be queried in a specific (hierarchical) order, so make sure you don’t override the order criteria parameter in conjunction with this tag.

# Showing children

To show the children of the current element in the loop, use the {% children %} tag. When Craft gets to this tag, it will loop through the element’s children, applying the same template defined between your {% nav %} and {% endnav %} tags to those children.

If you want to show some additional HTML surrounding the children, but only in the event that the element actually has children, wrap your {% children %} tag with {% ifchildren %} and {% endifchildren %} tags.

TIP

The {% nav %} tag should only be used in times when you want to show elements in a hierarchy, and you want the DOM to express that hierarchy. If you want to loop through elements linearly, use Twig’s for tag instead.

# paginate

This tag makes it easy to paginate query results across multiple pages.

{% set query = craft.entries()
    .section('blog')
    .limit(10) %}

{% paginate query as pageInfo, pageEntries %}

{% for entry in pageEntries %}
    <article>
        <h1>{{ entry.title }}</h1>
        {{ entry.body }}
    </article>
{% endfor %}

{% if pageInfo.prevUrl %}<a href="{{ pageInfo.prevUrl }}">Previous Page</a>{% endif %}
{% if pageInfo.nextUrl %}<a href="{{ pageInfo.nextUrl }}">Next Page</a>{% endif %}

Paginated URLs will be identical to the first page’s URL, except that “/p_X_” will be appended to the end (where X is the page number), e.g. http://my-project.test/news/p2.

TIP

You can use the pageTrigger config setting to customize what comes before the actual page number in your URLs. For example you could set it to 'page/', and your paginated URLs would start looking like http://my-project.test/news/page/2.

WARNING

Only a single {% paginate %} tag should be used per request.

# Parameters

The {% paginate %} tag has the following parameters:

# Query

The first thing you pass into the {% paginate %} tag is a query object (such as an element query), which defines all of the results that should be paginated. Use the limit parameter to define how many results should show up per page (100 by default).

WARNING

This parameter needs to be an actual query object, not an array of pre-fetched results. So don’t call all() on the query before passing it in.

# as

Next up you need to type “as”, followed by one or two variable names:

  • as pageInfo, pageEntries
  • as pageEntries

Here’s what they get set to:

  • pageInfo gets set to a craft\web\twig\variables\Paginate object, which provides info about the current page, and some helper methods for creating links to other pages. (See below for more info.)
  • pageEntries gets set to an array of the results (e.g. the elements) that belong to the current page.

TIP

If you only specify one variable name here, the pageInfo variable will be called paginate by default for backwards compatibility.

# Showing the results

The {% paginate %} tag won’t actually output the current page’s results for you. It will only give you an array of the results that should be on the current page (referenced by the variable you defined in the as parameter.)

Following your {% paginate %} tag, you will need to loop through this page’s results using a for tag.

{% paginate craft.entries.section('blog').limit(10) as pageEntries %}

{% for entry in pageEntries %}
    <article>
        <h1>{{ entry.title }}</h1>
        {{ entry.body }}
    </article>
{% endfor %}

# The pageInfo variable

The pageInfo variable (or whatever you’ve called it) provides the following properties and methods:

  • pageInfo.first – The offset of the first result on the current page.
  • pageInfo.last – The offset of the last result on the current page.
  • pageInfo.total – The total number of results across all pages
  • pageInfo.currentPage – The current page number.
  • pageInfo.totalPages – The total number of pages.
  • pageInfo.prevUrl – The URL to the previous page, or null if you’re on the first page.
  • pageInfo.nextUrl – The URL to the next page, or null if you’re on the last page.
  • pageInfo.firstUrl – The URL to the first page.
  • pageInfo.lastUrl – The URL to the last page.
  • pageInfo.getPageUrl( page ) – Returns the URL to a given page number, or null if the page doesn’t exist.
  • pageInfo.getPrevUrls( [dist] ) – Returns an array of URLs to the previous pages, with keys set to the page numbers. The URLs are returned in ascending order. You can optionally pass in the maximum distance away from the current page the function should go.
  • pageInfo.getNextUrls( [dist] ) – Returns an array of URLs to the next pages, with keys set to the page numbers. The URLs are returned in ascending order. You can optionally pass in the maximum distance away from the current page the function should go.
  • pageInfo.getRangeUrls( start, end ) – Returns an array of URLs to pages in a given range of page numbers, with keys set to the page numbers.

The pageInfo variable gives you lots of options for building the pagination navigation that’s right for you. Here are a few common examples.

If you just want simple Previous Page and Next Page links to appear, you can do this:

{% set query = craft.entries()
    .section('blog')
    .limit(10) %}

{% paginate query as pageInfo, pageEntries %}

{% if pageInfo.prevUrl %}<a href="{{ pageInfo.prevUrl }}">Previous Page</a>{% endif %}
{% if pageInfo.nextUrl %}<a href="{{ pageInfo.nextUrl }}">Next Page</a>{% endif %}

Note that we’re wrapping those links in conditionals because there won’t always be a previous or next page.

You can add First Page and Last Page links into the mix, you can do that too:

{% set query = craft.entries()
    .section('blog')
    .limit(10) %}

{% paginate query as pageInfo, pageEntries %}

<a href="{{ pageInfo.firstUrl }}">First Page</a>
{% if pageInfo.prevUrl %}<a href="{{ pageInfo.prevUrl }}">Previous Page</a>{% endif %}
{% if pageInfo.nextUrl %}<a href="{{ pageInfo.nextUrl }}">Next Page</a>{% endif %}
<a href="{{ pageInfo.lastUrl }}">Last Page</a>

There’s no reason to wrap those links in conditionals since there will always be a first and last page.

If you want to create a list of nearby pages, perhaps surrounding the current page number, you can do that too!

{% set query = craft.entries()
    .section('blog')
    .limit(10) %}

{% paginate query as pageInfo, pageEntries %}

<a href="{{ pageInfo.firstUrl }}">First Page</a>
{% if pageInfo.prevUrl %}<a href="{{ pageInfo.prevUrl }}">Previous Page</a>{% endif %}

{% for page, url in pageInfo.getPrevUrls(5) %}
    <a href="{{ url }}">{{ page }}</a>
{% endfor %}

<span class="current">{{ pageInfo.currentPage }}</span>

{% for page, url in pageInfo.getNextUrls(5) %}
    <a href="{{ url }}">{{ page }}</a>
{% endfor %}

{% if pageInfo.nextUrl %}<a href="{{ pageInfo.nextUrl }}">Next Page</a>{% endif %}
<a href="{{ pageInfo.lastUrl }}">Last Page</a>

In this example we’re only showing up to five page links in either direction of the current page. If you’d prefer to show more or less, just change the numbers that are passed into getPrevUrls() and getNextUrls(). You can also choose to not pass any number in at all, in which case all previous/next page URLs will be returned.

# redirect

This tag will redirect the browser to a different URL.

{% if not user or not user.isInGroup('members') %}
    {% redirect "pricing" %}
{% endif %}

# Parameters

The {% redirect %} tag has the following parameter:

# The URL

Immediately after typing “{% redirect”, you need to tell the tag where to redirect the browser. You can either give it a full URL, or just the path.

# The Status Code

By default, redirects will have 302 status codes, which tells the browser that the requested URL has only been moved to the redirected URL temporarily.

You can customize which status code accompanies your redirect response by typing it right after the URL. For example, the following code would return a 301 redirect (permanent):

{% redirect "pricing" 301 %}

# Flash Messages

You can optionally set flash messages that will show up for the user on the next request using the with notice and/or with error params:

{% if not currentUser.isInGroup('members') %}
    {% redirect "pricing" 301 with notice "You have to be a member to access that!" %}
{% endif %}

# requireGuest

This tag will ensure that the user is not logged in. If they’re already logged in, they’ll be redirected to the page specified by your postLoginRedirect config setting.

{% requireGuest %}

You can place this tag anywhere in your template, including within a conditional. If/when Twig gets to it, the guest enforcement will take place.

# requireLogin

This tag will ensure that the user is logged in. If they aren’t, they’ll be redirected to a Login page and returned to the original page after successfully logging in.

{% requireLogin %}

You can place this tag anywhere in your template, including within a conditional. If/when Twig gets to it, the login enforcement will take place.

The Login page location is based on your loginPath config setting. If you do not set loginPath, it defaults to login. That will throw a 404 error if you have not handled the /login route with a custom template. To use the control panel’s Login form, set it to admin/login or [your cpTrigger]/login.

# requirePermission

This tag will ensure that the current user is logged in with an account that has a given permission.

{% requirePermission 'stayUpLate' %}

The user can have the permission either directly or through one of their user groups. If they don’t have it, a 403 (Forbidden) error will be served.

See the Users page for a list of available permissions.

# switch

“Switch statements” offer a clean way to compare a variable against multiple possible values, instead of using several repetitive {% if %} conditionals.

Take this template for example, which is running different template code depending on a Matrix block’s type:

{% if matrixBlock.type == "text" %}

    {{ matrixBlock.textField|markdown }}

{% elseif matrixBlock.type == "image" %}

    {{ matrixBlock.image[0].getImg() }}

{% else %}

    <p>A font walks into a bar.</p>
    <p>The bartender says, “Hey, we don’t serve your type in here!”</p>

{% endif %}

Since all of the conditionals are evaluating the same thing – matrixBlock.type – we can simplify that code using a {% switch %} tag instead:

{% switch matrixBlock.type %}

    {% case "text" %}

        {{ matrixBlock.textField|markdown }}

    {% case "image" %}

        {{ matrixBlock.image[0].getImg() }}

    {% default %}

        <p>A font walks into a bar.</p>
        <p>The bartender says, “Hey, we don’t serve your type in here!”</p>

{% endswitch %}

If you’re using the {% switch %} tag inside of a {% for %} loop, you won’t be able to access Twig’s loop variable directly inside of the {% switch %} tag. Instead, you can access it like so:

{% for matrixBlock in entry.matrixField.all() %}
    {% set loopIndex = loop.index %}

    {% switch matrixBlock.type %}

        {% case "text" %}

            Loop #{{ loopIndex }}

    {% endswitch %}
{% endfor %}

Tip: This tag is a bit simpler than other languages’ switch implementations you may have seen: matching cases are automatically broken out of, so there’s no need for break statements.