Fundamental Twig for Front-End Development

Your comprehensive guide for Twig front-end view templates.
Daffodil (Narcissus species): flowering plants in grass. Watercolour
Daffodil (Narcissus species): flowering plants in grass. Watercolour. Public Domain Mark. Source: Wellcome Collection.

Read more

This post is an iteration on ERB and Twig Cross-Reference for Front-End Development. That post is geared towards developers who want to translate their Twig knowledge to ERB, or vice versa. You may also be interested in Fundamental ERB for Front-End Development.

Contents

What is Twig?

Twig is SensioLabs’ Django- / Jinja-like templating language for PHP. The recommended extension for Twig files is .twig, .<compiled_extension>.twig is useful, and .html —though inaccurate— is common in front-end templating. It’s used by SensioLabs’ Symfony; by Drupal 8, which is built on Symfony; and by Craft CMS.

Twig is a great language for building web front ends: it is full-featured without having more than one person could hope to learn, it reads fairly closely to English, and it has great official documentation. Twig is especially notable for its powerful support for complex inheritance across templates. Check out the use tag, the embed tag, and the block() function.

Twig even has Javascript implementations, making it easy to fit into projects built on the JS ecosystem. A quick overview to help you pick the one that best suits yours needs:

  • Mozilla’s Nunjucks is officially “jinja2 inspired” but it has [often followed Twig’s lead](https://github.com/mozilla/nunjucks/issues?utf8=✓&q=is%3Aissue is%3Aclosed twig ). If you use Gulp in your build tools, you can use gulp-nunjucks.
  • Twig.js is a popular JS port of Twig that sees more active development than Nunjucks does. It does not reach full parity with Twig (as of this writing Twig.js notably still has some bugs with Twig’s embed tag) but it currently comes closer than Nunjucks does and, since its goal is to duplicate Twig, it likely always will. The Twig.js Gulp plugin is gulp-twig.
  • Twing is a Twig engine for Node.js written in TypeScript which aims to always maintain complete parity with Twig. It is described as “a maintainability-first engine that passes 100% of the TwigPHP integration tests, is as close as possible to its code structure and expose an as-close-as-possible API.” Because Twing is able to essentially reuse much of Twig’s codebase, adding new features as they are merged into Twig is straightforward. Twing is the youngest of these projects… Twig users, show it your love! gulp-twing lets you use Twing with Gulp.

To learn Twig, read through the official documentation, and try things out in twigfiddle.

Reference

Delimiters

Comments

Inline comments

{# … #}

twig
twig
{# comment #}
twig
{# comment #}

Block comments

{# … #}

twig
twig
{#
block comment
#}
twig
{#
block comment
#}

or

twig
twig
not a comment {# block
comment #} not a comment
twig
not a comment {# block
comment #} not a comment

Outputting values

{{ }}

twig
twig
{{ "print this" }} {# output: `print this` #}
{{ 1 + 2 }} {# output: `3` #}
twig
{{ "print this" }} {# output: `print this` #}
{{ 1 + 2 }} {# output: `3` #}

Execution (Control Code)

{% … %}

twig
twig
{% if … %} … {% endif %}
twig
{% if … %} … {% endif %}

Conditionals

ifelseifendif

twig
twig
{% if x %}
y
{% elseif z == n %}{# note the spelling of elseif #}
0
{% else %}
1
{% endif %}
twig
{% if x %}
y
{% elseif z == n %}{# note the spelling of elseif #}
0
{% else %}
1
{% endif %}

With logical operators

Twig supports “condition ? iftrue : iffalse”, and “ifselftrue ?: otherwise”.

twig
twig
{# assuming x, y, z, and n are defined and/or Twig's strict variables option is turned off #}
{# if x then y #}
{{ x ? y }}
{# if x is true, y. otherwise, if z equals n then 0. otherwise 1 #}
{{ x ? y : z == n ? 0 : 1 }}
{# ternary operator: x if x is true, otherwise y #}
{{ x ?: y }}
twig
{# assuming x, y, z, and n are defined and/or Twig's strict variables option is turned off #}
{# if x then y #}
{{ x ? y }}
{# if x is true, y. otherwise, if z equals n then 0. otherwise 1 #}
{{ x ? y : z == n ? 0 : 1 }}
{# ternary operator: x if x is true, otherwise y #}
{{ x ?: y }}

Truth and falsity of zero in Boolean contexts

As in PHP, 0 is False in Boolean contexts

twig
twig
{{ false ? 'truthy' : 'falsy' }} {# output: `falsy` #}
{{ 0 ? 'truthy' : 'falsy' }} {# output: `falsy` #}
twig
{{ false ? 'truthy' : 'falsy' }} {# output: `falsy` #}
{{ 0 ? 'truthy' : 'falsy' }} {# output: `falsy` #}

Defining variables

set

twig
twig
{% set var = 1 %}
{% set anotherVar = 0 %}
{% set falseVar = false %}
{{ var ? 2 }} {# output: `2` #}
{{ anotherVar ? 2 }} {# output: null - Twig, unlike PHP, equates 0 with falsehood #}
{{ falseVar ? '' : 2 }} {# output `2` #}
twig
{% set var = 1 %}
{% set anotherVar = 0 %}
{% set falseVar = false %}
{{ var ? 2 }} {# output: `2` #}
{{ anotherVar ? 2 }} {# output: null - Twig, unlike PHP, equates 0 with falsehood #}
{{ falseVar ? '' : 2 }} {# output `2` #}

Twig can define multiple variables in a single call — just keep in mind that developers not used to this might overlook the multiple declarations!

twig
twig
{% set x, y, z = 1, 2, 3 %}
twig
{% set x, y, z = 1, 2, 3 %}

(A value must be explicitly provided for each variable: {% set x, y = 1 %} will error.)

Line breaks within a variable’s value

Use the set tag’s form set xendset to capture chunks of text

twig
twig
{% set longVar %}
<div>
</div>
{% endset %}
{{ longVar }}
twig
{% set longVar %}
<div>
</div>
{% endset %}
{{ longVar }}

Dealing with undefined variables

  • is defined

    Especially useful when Twig’s strict variables option is turned on, in which case referring to an undefined variable will throw an error.

    twig
    twig
    {# output: Twig_Error_Runtime: Variable "x" does not exist. #}
    {{ x }}
    {# output: the content if var is defined #}
    {% if var is defined %}
    {% endif %}
    {# output: `advance` if var is defined, otherwise `fallback` #}
    {{ var is defined ? advance : fallback }}
    twig
    {# output: Twig_Error_Runtime: Variable "x" does not exist. #}
    {{ x }}
    {# output: the content if var is defined #}
    {% if var is defined %}
    {% endif %}
    {# output: `advance` if var is defined, otherwise `fallback` #}
    {{ var is defined ? advance : fallback }}
  • ??, the null coalescing operator

    twig
    twig
    {# output: `var` if it is defined and not null, otherwise `fallback` #}
    {{ var ?? fallback }}
    {# common use cases:
    1. output a variable only if it is defined #}
    {{ var ?? null }}
    {# set a variable with a fallback #}
    {% set x = y ?? null %}
    twig
    {# output: `var` if it is defined and not null, otherwise `fallback` #}
    {{ var ?? fallback }}
    {# common use cases:
    1. output a variable only if it is defined #}
    {{ var ?? null }}
    {# set a variable with a fallback #}
    {% set x = y ?? null %}

Variable interpolation

#{var}

twig
twig
{% set x = 1 %}
{{ "this is interpolated #{x}" }}{# output: `this is interpolated: 1` #}
twig
{% set x = 1 %}
{{ "this is interpolated #{x}" }}{# output: `this is interpolated: 1` #}

Concatenation

~ (tilde). Note that strings and numbers can be freely concatenated.

twig
twig
{% set string_variable = 'world' %}
{% set number_variable = 2 %}
{{ 'hello ' ~ string_variable }} {# output: `hello world` #}
{{ "example #{number_variable}" }} {# output: `example 2` #}
{{ 'example ' ~ 3 }} {# output: `example 3` #}
twig
{% set string_variable = 'world' %}
{% set number_variable = 2 %}
{{ 'hello ' ~ string_variable }} {# output: `hello world` #}
{{ "example #{number_variable}" }} {# output: `example 2` #}
{{ 'example ' ~ 3 }} {# output: `example 3` #}

Iteration (loops)

Iterating over items

for i in nendfor

twig
twig
{% set items = ['a','b','c'] %}
{# output: `...` #}
{% for i in 0..items.length %}.{% endfor %}
{# output: `a b c ` #}
{% for item in items %}
{{item}}
{% endfor %}
twig
{% set items = ['a','b','c'] %}
{# output: `...` #}
{% for i in 0..items.length %}.{% endfor %}
{# output: `a b c ` #}
{% for item in items %}
{{item}}
{% endfor %}

Using the loop index, 0-indexed

loop.index0

twig
twig
{% for item in items %}
{{loop.index0}}. {{item}}
{% endfor %}
twig
{% for item in items %}
{{loop.index0}}. {{item}}
{% endfor %}

Using the loop index, 1-indexed

loop.index

twig
twig
{% for item in items %}
{{loop.index}}. {{item}}
{% endfor %}
twig
{% for item in items %}
{{loop.index}}. {{item}}
{% endfor %}

Iterating a certain number of times

for i in nendfor

twig
twig
{% set items = ['a','b','c'] %}
{# output: `...` #}
{% for i in 0..items.length %}.{% endfor %}
{# output: `a b c ` #}
{% for item in items %}
{{item}}
{% endfor %}
twig
{% set items = ['a','b','c'] %}
{# output: `...` #}
{% for i in 0..items.length %}.{% endfor %}
{# output: `a b c ` #}
{% for item in items %}
{{item}}
{% endfor %}

Inspecting data

  • The |json_encode() filter formats an object’s data.

    twig
    twig
    {# for some object `posts` #}
    {{ posts|json_encode }}
    twig
    {# for some object `posts` #}
    {{ posts|json_encode }}
  • The dump() function outputs information about a variable.

    twig
    twig
    {# for some object `posts` #}
    {{ dump(posts) }}
    twig
    {# for some object `posts` #}
    {{ dump(posts) }}

    Note: dump must be enabled. Some implementations make it available out of the box (for example, Craft CMS in dev mode).

Slicing

|slice(start,count) or [start:count]

twig
twig
{{ [1,2,3,4]|slice(1) }} {# output: `Array` #}
{{ [1,2,3,4]|slice(1,2) }} {# output: `Array` #}
twig
{{ [1,2,3,4]|slice(1) }} {# output: `Array` #}
{{ [1,2,3,4]|slice(1,2) }} {# output: `Array` #}

Note: The output of the above Twig examples is Array, because in Twig the output of {{ [anArray] }} is Array. If you need to print an array, use |json_encode:

twig
twig
{{ [1,2,3,4]|slice(1)|json_encode() }} {# output: `[2,3,4]` #}
{{ [1,2,3,4]|slice(1,2)|json_encode() }} {# output: `[2,3]` #}
twig
{{ [1,2,3,4]|slice(1)|json_encode() }} {# output: `[2,3,4]` #}
{{ [1,2,3,4]|slice(1,2)|json_encode() }} {# output: `[2,3]` #}

In execution, no special steps are necessary:

twig
twig
{% set myArray = [1,2,3,4] %}
twig
{% set myArray = [1,2,3,4] %}

Shorthand to slice the first count items

[:count]

twig
twig
{{ [1,2,3,4][:2]|json_encode() }} {# output: `[1,2]` #}
twig
{{ [1,2,3,4][:2]|json_encode() }} {# output: `[1,2]` #}

Shorthand for everything after the start item

[start:]

twig
twig
{{ [1,2,3,4][2:]|json_encode() }} {# output: `[3,4]` #}
twig
{{ [1,2,3,4][2:]|json_encode() }} {# output: `[3,4]` #}

Trimming whitespace

Trim leading or trailing whitespace by adding a - inside in an opening or close delimiter, respectively:

twig
twig
{% something -%}
1
{%- something_else -%}
2
{%- last_thing %}
twig
{% something -%}
1
{%- something_else -%}
2
{%- last_thing %}

is equivalent to

twig
twig
{% something %}1{% something_else %}2{% last_thing %}
twig
{% something %}1{% something_else %}2{% last_thing %}

Trimming space between HTML elements

Twig doesn’t care what language you are compiling to, but it does provide a special spaceless tag for use with HTML.

twig
twig
{% spaceless %}
<div>…</div>
<span>…</span>
{% endspaceless %}
twig
{% spaceless %}
<div>…</div>
<span>…</span>
{% endspaceless %}

is equivalent to

twig
twig
<div>…</div><span>…</span>
twig
<div>…</div><span>…</span>

Note that this spaceless has limited powers:

  • it isn’t recursive

    twig
    twig
    {% spaceless %}
    <div>
    <div>
    </div>
    <div>
    <span>…</span>
    {% endspaceless %}
    twig
    {% spaceless %}
    <div>
    <div>
    </div>
    <div>
    <span>…</span>
    {% endspaceless %}

    is equivalent to

    twig
    twig
    <div><div>
    </div><div><span>…</span>
    twig
    <div><div>
    </div><div><span>…</span>
  • and content between HTML tags will disrupt it

    twig
    twig
    {% spaceless %}
    <div>…</div>
    sorry, spaceless
    <span>…</span>
    {% endspaceless %}
    twig
    {% spaceless %}
    <div>…</div>
    sorry, spaceless
    <span>…</span>
    {% endspaceless %}

    is equivalent to

    twig
    twig
    <div>…</div>
    sorry, spaceless
    <span>…</span>
    twig
    <div>…</div>
    sorry, spaceless
    <span>…</span>

Keyed values

Use dot notation or subscript syntax to access attributes of a variable:

twig
twig
{% set myVar = {hello: 'world'} %}
{{ myVar.hello }} {# output: world #}
{{ myVar['hello'] }} {# output: world #}
twig
{% set myVar = {hello: 'world'} %}
{{ myVar.hello }} {# output: world #}
{{ myVar['hello'] }} {# output: world #}

Vertical inheritance

For a layout file that pulls in page:

block + extends in child, block in parent.

layout.html.twig

twig
twig
{% block myBlock '' %}
{# or #}
{% block myBlock %}{% endblock %}
{# or #}
{% block myBlock %}{% endblock myBlock %}
twig
{% block myBlock '' %}
{# or #}
{% block myBlock %}{% endblock %}
{# or #}
{% block myBlock %}{% endblock myBlock %}

page.html.twig

twig
twig
{% extends 'layout.html.twig' %}
{% block myBlock %}
the content
{% endblock %}
twig
{% extends 'layout.html.twig' %}
{% block myBlock %}
the content
{% endblock %}

or if all the content is a variable x, page.html.twig

twig
twig
{% extends 'layout.html.twig' %}
{% block myBlock x %}
twig
{% extends 'layout.html.twig' %}
{% block myBlock x %}

or if all the content is a single string, page.html.twig

twig
twig
{% extends 'layout.html.twig' %}
{% block myBlock "#{x} content" %}
{# or #}
{% extends 'layout.html.twig' %}
{% block myBlock x ~ "content" %}
twig
{% extends 'layout.html.twig' %}
{% block myBlock "#{x} content" %}
{# or #}
{% extends 'layout.html.twig' %}
{% block myBlock x ~ "content" %}

or if all the content is a single literal string, page.html.twig

twig
twig
{% extends 'layout.html.twig' %}
{% block myBlock 'the content' %}
{# or #}
{% block myBlock "the content" %}
twig
{% extends 'layout.html.twig' %}
{% block myBlock 'the content' %}
{# or #}
{% block myBlock "the content" %}

Vertical inheritance with default content in the parent

main.html.twig

twig
twig
{% block content %}
default content
{% block sub_content '' %}
{% endblock %}
twig
{% block content %}
default content
{% block sub_content '' %}
{% endblock %}

override-content.html.twig

twig
twig
{% extends 'main.html.twig' %}
{% block content %}
the content
{% endblock %}
twig
{% extends 'main.html.twig' %}
{% block content %}
the content
{% endblock %}

Result of override-content.html.twig:

default content
default content

override-subcontent.html.twig

twig
twig
{% extends 'main.html.twig' %}
{% block subcontent %}
the sub-content
{% endblock %}
twig
{% extends 'main.html.twig' %}
{% block subcontent %}
the sub-content
{% endblock %}

Result of override-subcontent.html.twig:

default content
the sub-content
default content
the sub-content

Using partials

  • include tag

    twig
    twig
    {% include 'path/to/x' %}
    twig
    {% include 'path/to/x' %}
  • include function

    twig
    twig
    {{ include('path/to/x') }}
    twig
    {{ include('path/to/x') }}

The include tag passes the entire parent context to the included file by default:

twig
twig
{% set a = 1 %}
{% set b = 2 %}
{% include 'path/to/x' %} {# in path/to/x a=1 and b=2 #}
twig
{% set a = 1 %}
{% set b = 2 %}
{% include 'path/to/x' %} {# in path/to/x a=1 and b=2 #}

To pass only certain data, use include with only:

twig
twig
{% set a = 1 %}
{% set b = 2 %}
{% include 'path/to/x' with {a:a} only %}
{# in path/to/x a=1 and b does not exist #}
twig
{% set a = 1 %}
{% set b = 2 %}
{% include 'path/to/x' with {a:a} only %}
{# in path/to/x a=1 and b does not exist #}

Rename variables in the with (can be combined with only):

twig
twig
{% set a = 1 %}
{% include 'path/to/x' with {y:a} %} {# in path/to/x a=1 and y=1 #}
{% include 'path/to/z' with {y:a} only %}
{# in path/to/z y=1 and a does not exist #}
twig
{% set a = 1 %}
{% include 'path/to/x' with {y:a} %} {# in path/to/x a=1 and y=1 #}
{% include 'path/to/z' with {y:a} only %}
{# in path/to/z y=1 and a does not exist #}

Articles You Might Enjoy

Or Go To All Articles