Twig
Use the Twig templating language to format and transform CMS content.
Elements CMS supports the Twig templating language — a fast, secure, and flexible template engine widely used in modern PHP applications. Twig is what makes references like {{item.title}} work, and what powers everything from date formatting to control flow inside your CMS templates.
If you've ever written a template in Liquid, Jinja, or Handlebars, Twig will feel familiar. If not, the basics are quick to pick up — they're covered below.
The four building blocks
Twig has four kinds of syntax you'll use over and over:
{{ … }}— output a value. Anything between the double braces is evaluated and printed.{% … %}— a statement like anif, aforloop, or a variable assignment. Statements don't print anything by themselves.{# … #}— a comment that's stripped before output.|filter— a filter applied to a value.{{item.title|upper}}runs the title through theupperfilter.
You can chain filters: {{item.title|striptags|upper}} strips HTML tags then uppercases the result.
Where Twig runs in Elements
Inside a Collection or Item drop zone, {{item.title}} and friends work natively in any Elements component — Headings, Typography, Text, Link, Image — without any extra setup. The component reads the reference and renders the value.
When you want to use Twig syntax that goes beyond a simple reference — control flow, filters, custom HTML — you have two options:
In a custom HTML or Code component, wrap the Twig in an
@raw()directive. Without@raw(), the surrounding template engine treats{% %}and{{ }}as its own syntax and may interfere.In a native Elements component's text field, simple references and filters work directly. Control flow doesn't.
@raw()
{% if item.featured %}
<span class="badge">Featured</span>
{% endif %}
@endrawControl flow
Conditionals
Loops
The {% else %} block inside a {% for %} runs when the collection is empty — handy for "no items" fallbacks.
Twig exposes a loop variable inside any loop with useful counters:
loop.index— the current iteration, starting at 1.loop.index0— the same, starting at 0.loop.firstandloop.last— booleans for the first and last iterations.loop.length— the total number of items.
The most useful filters in CMS context
These come up constantly when working with Markdown content.
date — format a date
The format string uses PHP date tokens. Twig also offers a date_modify filter for relative date math.
The Twig International Date Extension is installed, but it requires PHP's intl extension to be enabled on your server to function correctly. Check your PHP configuration if locale-aware date formatting fails.
default — fall back when a value is missing
If item.author is missing or null, the filter substitutes the fallback. Without |default, missing properties output nothing.
length — count items in an array or characters in a string
upper, lower, title, capitalize — change case
striptags — strip HTML
Useful when reusing the rendered body in a <meta> description or excerpt.
slice — take part of an array or string
split and join — break apart and reassemble
escape (alias e) — make a string safe for output
Twig auto-escapes most output, but if you've manually marked a value as |raw and need to re-escape part of it, |escape does the trick.
Chaining: a clean text excerpt
A common recipe — take the body, strip HTML, take the first 32 words, and add an ellipsis:
The ~ operator concatenates strings, equivalent to + in JavaScript.
Variable assignment
{% set %} declares a variable that's only in scope for the rest of the template. Useful when you'd otherwise repeat a long expression.
Working with arrays
Common pitfalls
My {{ … }} reference renders literally on the page.
You're inside a code or HTML component without @raw(). Wrap the block in @raw() … @endraw so the outer engine doesn't pre-process the Twig syntax.
A property that exists in some posts but not others is breaking my template.
Use |default or wrap access in an {% if %}. Twig errors on undefined properties unless you guard against them.
My HTML is being auto-escaped — < shows up as <.
Twig auto-escapes string output by default. Use |raw to opt out of escaping for a specific value (only for content you trust):
Be careful — anything user-supplied should stay escaped to prevent cross-site scripting.
Date filters output 1970-01-01.
Your frontmatter date value is missing or unparseable. See Frontmatter for the supported date formats.
Useful resources
Twig tag reference (for
{% if %},{% for %},{% set %}, etc.)Twig playground — paste templates and test them interactively.
PHP date format reference — every token you can use with the
datefilter.
Last updated
Was this helpful?

