{
  "version": "https://jsonfeed.org/version/1.1",
  "title": "Cecil - Functions and filters",
  "home_page_url": "https://cecil.app/documentation/templates/reference/",
  "feed_url": "https://cecil.app/documentation/templates/reference/feed.json",
  "description": "Reference of the Twig functions, sorts and filters provided by Cecil.",
  "icon": "https://cecil.app/favicon.7ff4b89eec6dad139a7d2d6561543285.png",
  "favicon": "https://cecil.app/thumbnails/64x/favicon.7ff4b89eec6dad139a7d2d6561543285.png",
  "language": "en",
  "items": [
    {
      "id": "https://cecil.app/documentation/templates/reference/functions/",
      "url": "https://cecil.app/documentation/templates/reference/functions/",
      "title": "Functions",
      "summary": "url, html, readtime, hash, cache_key, getenv, dump, etc.",
      "date_published": "2021-05-07T00:00:00+00:00",
      "date_modified": "2026-10-05T00:00:00+00:00","content_text": "Functions\n\nFunctions can be called to generate content. Functions are called by their name followed by parentheses (()) and may have arguments.\n\nurl\nCreates a valid URL for a page, a menu entry, an asset, a page ID or a path.\n{{ url(value, {options}) }}\n\n\n\nOption\nDescription\nType\nDefault\n\n\n\n\ncanonical\nPrefix URL with baseurl or use canonical.url if exists.\nboolean\nfalse\n\n\nformat\nDefines page output format (e.g.: json).\nstring\nhtml\n\n\nlanguage\nDefines page language (e.g.: fr).\nstring\nnull\n\n\n\nExamples:\n{# page #}\n{{ url(page) }}\n{{ url(page, {canonical: true}) }}\n{{ url(page, {format: json}) }}\n{{ url(page, {language: fr}) }}\n{# menu entry #}\n{{ url(site.menus.main.about) }}\n{# asset #}\n{{ url(asset('styles.css')) }}\n{# page ID #}\n{{ url('page-id') }}\n{# path #}\n{{ url('about-me\/') }}\n{{ url('tags\/' ~ tag) }}\nFor convenience the url function is also available as a filter:\n{# page #}\n{{ page|url }}\n{{ page|url({canonical: true, format: json, language: fr}) }}\n{# asset #}\n{{ asset('styles.css')|url }}\nWhen the value is a string, url() slugifies it to find a matching page ID (e.g.: url('tags\/My Tag') returns the URL of the page tags\/my-tag). If no page matches, the string is kept as a path, with invalid characters (e.g.: spaces) percent-encoded.\nhtml\nCreates an HTML element from an asset (or an array of assets with custom attributes).\n{{ html(asset, {attributes}, {options}) }}\n{# dedicated functions for each common type of asset #}\n{{ css(asset) }}\n{{ js(asset) }}\n{{ image(asset) }}\n{{ audio(asset) }}\n{{ video(asset) }}\n\n\n\nOption\nDescription\nType\n\n\n\n\nattributes\nAdds name=\"value\" couple to the HTML element.\narray\n\n\noptions\n{preload: boolean}: preloads.For images:{formats: array}: adds alternative formats.{responsive: bool|string}: adds responsive images (based on width or pixels density).{placeholder: string}: fills the image background before loading (color or lqip).\narray\n\n\n\nSince version 8.42.0, the html function replace the deprecated html filter.\nYou can define a global default behavior of images options (formats, responsive and placeholder) through the layouts configuration.\nWhen layouts.images.dark_suffix is configured (e.g. .dark), Cecil automatically looks for a dark variant of each image (e.g. photo.dark.jpg alongside photo.jpg) and generates a &lt;picture&gt; element with a &lt;source media=\"(prefers-color-scheme: dark)\"&gt;.\nIn the same way, when layouts.images.mobile_suffix is configured (e.g. .mobile), Cecil looks for a mobile variant of each image (e.g. photo.mobile.jpg) and adds a &lt;source&gt; with the layouts.images.mobile_media_query media query. If a dark variant of the mobile image exists (e.g. photo.mobile.dark.jpg), it is used on mobile with dark color scheme.\nExamples:\n{# CSS with an attribute #}\n{{ html(asset('print.css'), {media: 'print'}) }}\n{# CSS with an attribute and an option #}\n{{ html(asset('styles.css'), {title: 'Main theme'}, {preload: true}) }}\n{# Array of assets with media query #}\n{{ html([\n  {asset: asset('css\/style.css')},\n  {asset: asset('css\/style-dark.css'), attributes: {media: '(prefers-color-scheme: dark)'}}\n]) }}\n{# JavaScript #}\n{{ html(asset('script.js')) }}\n{# image without specific attributes nor options #}\n{{ html(asset('image.png')) }}\n{# image with specific attributes, responsive images and alternative formats #}\n{{ html(asset('image.jpg'), {alt: 'Description', loading: 'lazy'}, {responsive: true, formats: ['avif', 'webp']}) }}\n{# image with responsive pixels density images #}\n{{ html(asset('image.jpg'), options={responsive: 'density'}, attributes={width: 256}) }}\n{# image with a Low-Quality Image Placeholder #}\n{{ html(asset('image.jpg'), {alt: 'Description', loading: 'lazy'}, {placeholder: 'lqip'}) }}\n{# Audio #}\n{{ html(asset('audio.mp3')) }}\n{# Video #}\n{{ html(asset('video.mp4')) }}\nFor convenience the html function stay available as a filter (but is considered as deprecated):\n{{ asset|html({attributes}, {options}) }}\nreadtime\nDetermines read time of a text, in minutes.\n{{ readtime(value) }}\nExample:\n{{ readtime(page.content) }} min\nhash\nCalculates the hash of an object, an array or a string with a given algorithm.\n{{ hash(value, algorithm) }}\nalgorithm can be any algorithm supported by PHP's hash() function (e.g.: md5, sha256, etc.). Default is xxh128.\nExample:\n{{ hash('my string', 'sha256') }}\ncache_key\nCalculates a cache key for fragments cache based on a name and an optional value.\n{% cache cache_key(name, value) %}\n  {# cacheable content #}\n{% endcache %}\nThe function adds a hash of the value (could be a string, an array or an object) to the name (and the current language and build ID to be sure the generated cache key is unique) so if the value is changed the cache key is changed too and the cache is automatically cleared.\ngetenv\nGets the value of an environment variable from its key.\n{{ getenv(var) }}\nExample:\n{{ getenv('VAR') }}\ndump\nThe dump function dumps information about a template variable. This is mostly useful to debug a template that does not behave as expected by introspecting its variables:\n{{ dump(user) }}\nThe debug mode must be enabled.\nd\nThe d() function is the HTML version of dump() and use the Symfony VarDumper Component behind the scenes.\n{{ d(variable, {theme: light}) }}\n\nIf variable is not provided then the function returns the current Twig context\nAvailable themes are « light » (default) and « dark »\n\nThe debug mode must be enabled.",
      "content_html": "<h1>Functions</h1>\n<blockquote>\n<p><a href=\"https://twig.symfony.com/doc/functions/index.html\" target=\"_blank\" rel=\"noopener noreferrer\">Functions</a> can be called to generate content. Functions are called by their name followed by parentheses (<code translate=\"no\">()</code>) and may have arguments.</p>\n</blockquote>\n<h2 id=\"url\">url</h2>\n<p>Creates a valid URL for a page, a menu entry, an asset, a page ID or a path.</p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ url(value, {options}) }}</span></code></pre>\n<table>\n<thead>\n<tr>\n<th>Option</th>\n<th>Description</th>\n<th>Type</th>\n<th>Default</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>canonical</td>\n<td>Prefix URL with <a href=\"../../configuration/22-site.md#baseurl\"><code translate=\"no\">baseurl</code></a> or use <a href=\"../../configuration/22-site.md#metatags-options\"><code translate=\"no\">canonical.url</code></a> if exists.</td>\n<td>boolean</td>\n<td><code translate=\"no\">false</code></td>\n</tr>\n<tr>\n<td>format</td>\n<td>Defines page <a href=\"../../configuration/29-output.md#output-formats\">output format</a> (e.g.: <code translate=\"no\">json</code>).</td>\n<td>string</td>\n<td><code translate=\"no\">html</code></td>\n</tr>\n<tr>\n<td>language</td>\n<td>Defines page <a href=\"../../configuration/23-languages.md#language\">language</a> (e.g.: <code translate=\"no\">fr</code>).</td>\n<td>string</td>\n<td>null</td>\n</tr>\n</tbody>\n</table>\n<p><em>Examples:</em></p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-comment\">{# page #}</span><span class=\"xml\">\n</span><span class=\"hljs-template-variable\">{{ url(page) }}</span><span class=\"xml\">\n</span><span class=\"hljs-template-variable\">{{ url(page, {canonical: true}) }}</span><span class=\"xml\">\n</span><span class=\"hljs-template-variable\">{{ url(page, {format: json}) }}</span><span class=\"xml\">\n</span><span class=\"hljs-template-variable\">{{ url(page, {language: fr}) }}</span><span class=\"xml\">\n</span><span class=\"hljs-comment\">{# menu entry #}</span><span class=\"xml\">\n</span><span class=\"hljs-template-variable\">{{ url(site.menus.main.about) }}</span><span class=\"xml\">\n</span><span class=\"hljs-comment\">{# asset #}</span><span class=\"xml\">\n</span><span class=\"hljs-template-variable\">{{ url(asset('styles.css')) }}</span><span class=\"xml\">\n</span><span class=\"hljs-comment\">{# page ID #}</span><span class=\"xml\">\n</span><span class=\"hljs-template-variable\">{{ url('page-id') }}</span><span class=\"xml\">\n</span><span class=\"hljs-comment\">{# path #}</span><span class=\"xml\">\n</span><span class=\"hljs-template-variable\">{{ url('about-me/') }}</span><span class=\"xml\">\n</span><span class=\"hljs-template-variable\">{{ url('tags/' ~ tag) }}</span></code></pre>\n<aside class=\"note note-info\"><p>For convenience the <code translate=\"no\">url</code> function is also available as a filter:</p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-comment\">{# page #}</span><span class=\"xml\">\n</span><span class=\"hljs-template-variable\">{{ page|url }}</span><span class=\"xml\">\n</span><span class=\"hljs-template-variable\">{{ page|url({canonical: true, format: json, language: fr}) }}</span><span class=\"xml\">\n</span><span class=\"hljs-comment\">{# asset #}</span><span class=\"xml\">\n</span><span class=\"hljs-template-variable\">{{ asset('styles.css')|url }}</span></code></pre></aside>\n<aside class=\"note note-tip\"><p>When the value is a string, <code translate=\"no\">url()</code> slugifies it to find a matching page ID (e.g.: <code translate=\"no\">url('tags/My Tag')</code> returns the URL of the page <code translate=\"no\">tags/my-tag</code>). If no page matches, the string is kept as a path, with invalid characters (e.g.: spaces) percent-encoded.</p></aside>\n<h2 id=\"html\">html</h2>\n<p>Creates an HTML element from an asset (or an array of assets with custom attributes).</p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ html(asset, {attributes}, {options}) }}</span><span class=\"xml\">\n</span><span class=\"hljs-comment\">{# dedicated functions for each common type of asset #}</span><span class=\"xml\">\n</span><span class=\"hljs-template-variable\">{{ css(asset) }}</span><span class=\"xml\">\n</span><span class=\"hljs-template-variable\">{{ js(asset) }}</span><span class=\"xml\">\n</span><span class=\"hljs-template-variable\">{{ image(asset) }}</span><span class=\"xml\">\n</span><span class=\"hljs-template-variable\">{{ audio(asset) }}</span><span class=\"xml\">\n</span><span class=\"hljs-template-variable\">{{ video(asset) }}</span></code></pre>\n<table>\n<thead>\n<tr>\n<th>Option</th>\n<th>Description</th>\n<th>Type</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>attributes</td>\n<td>Adds <code translate=\"no\">name=\"value\"</code> couple to the HTML element.</td>\n<td>array</td>\n</tr>\n<tr>\n<td>options</td>\n<td><code translate=\"no\">{preload: boolean}</code>: preloads.<br>For images:<br><code translate=\"no\">{formats: array}</code>: adds alternative formats.<br><code translate=\"no\">{responsive: bool|string}</code>: adds responsive images (based on <code translate=\"no\">width</code> or pixels <code translate=\"no\">density</code>).<br><code translate=\"no\">{placeholder: string}</code>: fills the image background before loading (<code translate=\"no\">color</code> or <code translate=\"no\">lqip</code>).</td>\n<td>array</td>\n</tr>\n</tbody>\n</table>\n<aside class=\"note note-warning\"><p>Since version <ins>8.42.0</ins>, the <code translate=\"no\">html</code> function replace the deprecated <code translate=\"no\">html</code> filter.</p></aside>\n<aside class=\"note note-tip\"><p>You can define a global default behavior of images options (<code translate=\"no\">formats</code>, <code translate=\"no\">responsive</code> and <code translate=\"no\">placeholder</code>) through the <a href=\"../../configuration/28-layouts.md#layouts-images\">layouts configuration</a>.\nWhen <a href=\"../../configuration/28-layouts.md#layouts-images\"><code translate=\"no\">layouts.images.dark_suffix</code></a> is configured (e.g. <code translate=\"no\">.dark</code>), Cecil automatically looks for a dark variant of each image (e.g. <code translate=\"no\">photo.dark.jpg</code> alongside <code translate=\"no\">photo.jpg</code>) and generates a <code translate=\"no\">&lt;picture&gt;</code> element with a <code translate=\"no\">&lt;source media=\"(prefers-color-scheme: dark)\"&gt;</code>.\nIn the same way, when <a href=\"../../configuration/28-layouts.md#layouts-images\"><code translate=\"no\">layouts.images.mobile_suffix</code></a> is configured (e.g. <code translate=\"no\">.mobile</code>), Cecil looks for a mobile variant of each image (e.g. <code translate=\"no\">photo.mobile.jpg</code>) and adds a <code translate=\"no\">&lt;source&gt;</code> with the <a href=\"../../configuration/28-layouts.md#layouts-images\"><code translate=\"no\">layouts.images.mobile_media_query</code></a> media query. If a dark variant of the mobile image exists (e.g. <code translate=\"no\">photo.mobile.dark.jpg</code>), it is used on mobile with dark color scheme.</p></aside>\n<p><em>Examples:</em></p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-comment\">{# CSS with an attribute #}</span><span class=\"xml\">\n</span><span class=\"hljs-template-variable\">{{ html(asset('print.css'), {media: 'print'}) }}</span><span class=\"xml\">\n</span><span class=\"hljs-comment\">{# CSS with an attribute and an option #}</span><span class=\"xml\">\n</span><span class=\"hljs-template-variable\">{{ html(asset('styles.css'), {title: 'Main theme'}, {preload: true}) }}</span><span class=\"xml\">\n</span><span class=\"hljs-comment\">{# Array of assets with media query #}</span><span class=\"xml\">\n</span><span class=\"hljs-template-variable\">{{ html([\n  {asset: asset('css/style.css')},\n  {asset: asset('css/style-dark.css'), attributes: {media: '(prefers-color-scheme: dark)'}}</span><span class=\"xml\">\n]) }}\n</span><span class=\"hljs-comment\">{# JavaScript #}</span><span class=\"xml\">\n</span><span class=\"hljs-template-variable\">{{ html(asset('script.js')) }}</span><span class=\"xml\">\n</span><span class=\"hljs-comment\">{# image without specific attributes nor options #}</span><span class=\"xml\">\n</span><span class=\"hljs-template-variable\">{{ html(asset('image.png')) }}</span><span class=\"xml\">\n</span><span class=\"hljs-comment\">{# image with specific attributes, responsive images and alternative formats #}</span><span class=\"xml\">\n</span><span class=\"hljs-template-variable\">{{ html(asset('image.jpg'), {alt: 'Description', loading: 'lazy'}, {responsive: true, formats: ['avif', 'webp']}) }}</span><span class=\"xml\">\n</span><span class=\"hljs-comment\">{# image with responsive pixels density images #}</span><span class=\"xml\">\n</span><span class=\"hljs-template-variable\">{{ html(asset('image.jpg'), options={responsive: 'density'}, attributes={width: 256}) }}</span><span class=\"xml\">\n</span><span class=\"hljs-comment\">{# image with a Low-Quality Image Placeholder #}</span><span class=\"xml\">\n</span><span class=\"hljs-template-variable\">{{ html(asset('image.jpg'), {alt: 'Description', loading: 'lazy'}, {placeholder: 'lqip'}) }}</span><span class=\"xml\">\n</span><span class=\"hljs-comment\">{# Audio #}</span><span class=\"xml\">\n</span><span class=\"hljs-template-variable\">{{ html(asset('audio.mp3')) }}</span><span class=\"xml\">\n</span><span class=\"hljs-comment\">{# Video #}</span><span class=\"xml\">\n</span><span class=\"hljs-template-variable\">{{ html(asset('video.mp4')) }}</span></code></pre>\n<aside class=\"note note-info\"><p>For convenience the <code translate=\"no\">html</code> function stay available as a filter (but is considered as deprecated):</p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ asset|html({attributes}, {options}) }}</span></code></pre></aside>\n<h2 id=\"readtime\">readtime</h2>\n<p>Determines read time of a text, in minutes.</p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ readtime(value) }}</span></code></pre>\n<p><em>Example:</em></p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ readtime(page.content) }}</span><span class=\"xml\"> min</span></code></pre>\n<h2 id=\"hash\">hash</h2>\n<p>Calculates the hash of an object, an array or a string with a given algorithm.</p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ hash(value, algorithm) }}</span></code></pre>\n<p><code translate=\"no\">algorithm</code> can be any algorithm supported by PHP's <code translate=\"no\">hash()</code> function (e.g.: <code translate=\"no\">md5</code>, <code translate=\"no\">sha256</code>, etc.). Default is <code translate=\"no\">xxh128</code>.</p>\n<p><em>Example:</em></p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ hash('my string', 'sha256') }}</span></code></pre>\n<h2 id=\"cache-key\">cache_key</h2>\n<p>Calculates a cache key for <a href=\"../17-cache.md#fragments-cache\"><em>fragments</em> cache</a> based on a name and an optional value.</p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-tag\">{% <span class=\"hljs-name\">cache</span> cache_key(name, value) %}</span><span class=\"xml\">\n  </span><span class=\"hljs-comment\">{# cacheable content #}</span><span class=\"xml\">\n</span><span class=\"hljs-template-tag\">{% <span class=\"hljs-name\">endcache</span> %}</span></code></pre>\n<p>The function adds a hash of the value (could be a string, an array or an object) to the name (and the current language and build ID to be sure the generated cache key is unique) so if the value is changed the cache key is changed too and the cache is automatically cleared.</p>\n<h2 id=\"getenv\">getenv</h2>\n<p>Gets the value of an environment variable from its key.</p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ getenv(var) }}</span></code></pre>\n<p><em>Example:</em></p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ getenv('VAR') }}</span></code></pre>\n<h2 id=\"dump\">dump</h2>\n<p>The <code translate=\"no\">dump</code> function dumps information about a template variable. This is mostly useful to debug a template that does not behave as expected by introspecting its variables:</p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ <span class=\"hljs-name\">dump</span><span class=\"hljs-params\">(user)</span> }}</span></code></pre>\n<aside class=\"note note-important\"><p>The <a href=\"../../configuration/22-site.md#debug\"><em>debug mode</em></a> must be enabled.</p></aside>\n<h2 id=\"d\">d</h2>\n<p>The <code translate=\"no\">d()</code> function is the HTML version of <a href=\"#dump\"><code translate=\"no\">dump()</code></a> and use the <a href=\"https://symfony.com/doc/5.4/components/var_dumper.html\" target=\"_blank\" rel=\"noopener noreferrer\">Symfony VarDumper Component</a> behind the scenes.</p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ d(variable, {theme: light}) }}</span></code></pre>\n<ul>\n<li>If <em>variable</em> is not provided then the function returns the current Twig context</li>\n<li>Available themes are « light » (default) and « dark »</li>\n</ul>\n<aside class=\"note note-important\"><p>The <a href=\"../../configuration/22-site.md#debug\"><em>debug mode</em></a> must be enabled.</p></aside>",
      "language": "en"
    },
    {
      "id": "https://cecil.app/documentation/templates/reference/sorts/",
      "url": "https://cecil.app/documentation/templates/reference/sorts/",
      "title": "Sorts",
      "summary": "Sort collections of pages, menus or taxonomies.",
      "date_published": "2021-05-07T00:00:00+00:00",
      "date_modified": "2026-10-05T00:00:00+00:00","content_text": "Sorts\nSorting collections (of pages, menus or taxonomies).\nsort_by_title\nSorts a collection by title (with natural sort).\n{{ collection|sort_by_title }}\nExample:\n{{ site.pages|sort_by_title }}\nsort_by_date\nSorts a collection by date (most recent first).\n{{ collection|sort_by_date(variable='date', desc_title=false) }}\nExample:\n{# sort by date #}\n{{ site.pages|sort_by_date }}\n{# sort by updated variable instead of date #}\n{{ site.pages|sort_by_date(variable='updated') }}\n{# sort items with the same date by desc title #}\n{{ site.pages|sort_by_date(desc_title=true) }}\n{# reverse sort #}\n{{ site.pages|sort_by_date|reverse }}\nsort_by_weight\nSorts a collection by weight (lighter first).\n{{ collection|sort_by_weight }}\nExample:\n{{ site.menus.main|sort_by_weight }}\nsort\nFor more complex cases, you should use Twig’s native sort.\nExample:\n{% set files = site.static|sort((a, b) =&gt; a.date|date('U') &lt; b.date|date('U')) %}",
      "content_html": "<h1>Sorts</h1>\n<p>Sorting collections (of pages, menus or taxonomies).</p>\n<h2 id=\"sort-by-title\">sort_by_title</h2>\n<p>Sorts a collection by title (with <a href=\"https://en.wikipedia.org/wiki/Natural_sort_order\" target=\"_blank\" rel=\"noopener noreferrer\">natural sort</a>).</p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ collection|sort_by_title }}</span></code></pre>\n<p><em>Example:</em></p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ site.pages|sort_by_title }}</span></code></pre>\n<h2 id=\"sort-by-date\">sort_by_date</h2>\n<p>Sorts a collection by date (most recent first).</p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ collection|sort_by_date(variable='<span class=\"hljs-name\">date</span>', desc_title=false) }}</span></code></pre>\n<p><em>Example:</em></p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-comment\">{# sort by date #}</span><span class=\"xml\">\n</span><span class=\"hljs-template-variable\">{{ site.pages|sort_by_date }}</span><span class=\"xml\">\n</span><span class=\"hljs-comment\">{# sort by updated variable instead of date #}</span><span class=\"xml\">\n</span><span class=\"hljs-template-variable\">{{ site.pages|sort_by_date(variable='updated') }}</span><span class=\"xml\">\n</span><span class=\"hljs-comment\">{# sort items with the same date by desc title #}</span><span class=\"xml\">\n</span><span class=\"hljs-template-variable\">{{ site.pages|sort_by_date(desc_title=true) }}</span><span class=\"xml\">\n</span><span class=\"hljs-comment\">{# reverse sort #}</span><span class=\"xml\">\n</span><span class=\"hljs-template-variable\">{{ site.pages|sort_by_date|<span class=\"hljs-keyword\">reverse</span> }}</span></code></pre>\n<h2 id=\"sort-by-weight\">sort_by_weight</h2>\n<p>Sorts a collection by weight (lighter first).</p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ collection|sort_by_weight }}</span></code></pre>\n<p><em>Example:</em></p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ site.menus.main|sort_by_weight }}</span></code></pre>\n<h2 id=\"sort\">sort</h2>\n<p>For more complex cases, you should use <a href=\"https://twig.symfony.com/doc/filters/sort.html\" target=\"_blank\" rel=\"noopener noreferrer\">Twig’s native <code translate=\"no\">sort</code></a>.</p>\n<p><em>Example:</em></p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-tag\">{% <span class=\"hljs-name\"><span class=\"hljs-keyword\">set</span></span> files = site.static|<span class=\"hljs-keyword\">sort</span>((a, b) =&gt; a.<span class=\"hljs-name\">date</span>|<span class=\"hljs-keyword\">date</span>('U') &lt; b.<span class=\"hljs-name\">date</span>|<span class=\"hljs-keyword\">date</span>('U')) %}</span></code></pre>",
      "language": "en"
    },
    {
      "id": "https://cecil.app/documentation/templates/reference/filters/",
      "url": "https://cecil.app/documentation/templates/reference/filters/",
      "title": "Filters",
      "summary": "filter_by, markdown_to_html, toc, slugify, excerpt, highlight, preg_*, etc.",
      "date_published": "2021-05-07T00:00:00+00:00",
      "date_modified": "2026-10-05T00:00:00+00:00","content_text": "Filters\nVariables can be modified by filters. Filters are separated from the variable by a pipe symbol (|). Multiple filters can be chained. The output of one filter is applied to the next.\n{{ page.title|truncate(25)|capitalize }}\nfilter_by\nFilters a pages collection by variable name\/value.\n{{ collection|filter_by(variable, value) }}\nExample:\n{{ pages|filter_by('section', 'blog') }}\nfilter\nFor more complex cases, you should use Twig’s native filter.\nExample:\n{% pages|filter(p =&gt; p.virtual == false and p.id not in ['page-1', 'page-2']) %}\nmarkdown_to_html\nConverts a Markdown string to HTML.\n{{ markdown|markdown_to_html }}\n{% apply markdown_to_html %}\n{# Markdown here #}\n{% endapply %}\nExamples:\n{% set markdown = '**This is bold text**' %}\n{{ markdown|markdown_to_html }}\n{% apply markdown_to_html %}\n**This is bold text**\n{% endapply %}\ntoc\nExtracts only headings matching the given selectors (h2, h3, etc.), or those defined in config pages.body.toc if not specified.\nThe format parameter defines the output format: html or json.\nThe url parameter is used to build links to headings.\n{{ markdown|toc(format, selectors, url) }}\nExamples:\n{{ page.body|toc }}\n{{ page.body|toc('html') }}\n{{ page.body|toc(selectors=['h2']) }}\n{{ page.body|toc(url=url(page)) }}\njson_decode\nConverts a JSON string to an array.\n{{ json|json_decode }}\nExample:\n{% set json = '{\"foo\": \"bar\"}' %}\n{% set array = json|json_decode %}\n{{ array.foo }}\nyaml_parse\nConverts a YAML string to an array.\n{{ yaml|yaml_parse }}\nExample:\n{% set yaml = 'key: value' %}\n{% set array = yaml|yaml_parse %}\n{{ array.key }}\nslugify\nConverts a string to a slug.\n{{ string|slugify }}\nu\nThe u filter wraps a text in a Unicode object (a Symfony UnicodeString instance) that exposes methods to \"manipulate\" the string.\nExample:\n{{ 'cecil_string with twig'|u.camel.title }}\n\nCecilStringWithTwig\n\nsingular\nThe singular filter transforms a given noun in its plural form into its singular version.\n{{ string|singular(locale)}}\nExample:\n{# English (en) rules are used by default #}\n{{ 'partitions'|singular }}\n\npartition\n\n{{ 'partitions'|singular('fr') }}\n\npartition\n\nplural\nThe plural filter transforms a given noun in its singular form into its plural version.\n{{ string|plural(locale)}}\nExample:\n{# English (en) rules are used by default #}\n{{ 'animal'|plural }}\n\nanimals\n\n{{ 'animal'|plural('fr') }}\n\nanimaux\n\nexcerpt\nTruncates a string and appends suffix.\n{{ string|excerpt(length, suffix) }}\n\n\n\nOption\nDescription\nType\nDefault\n\n\n\n\nlength\nTruncates after this number of characters.\ninteger\n450\n\n\nsuffix\nAppends characters.\nstring\n…\n\n\n\nExamples:\n{{ variable|excerpt }}\n{{ variable|excerpt(250, '...') }}\nexcerpt_html\nReads characters before or after &lt;!-- excerpt --&gt; or &lt;!-- break --&gt; tag.\nSee Content documentation for details.\n{{ string|excerpt_html({separator, capture}) }}\n\n\n\nOption\nDescription\nType\nDefault\n\n\n\n\nseparator\nString to use as separator.\nstring\nexcerpt|break\n\n\ncapture\nPart to capture, before or after the separator.\nstring\nbefore\n\n\n\nExamples:\n{{ variable|excerpt_html }}\n{{ variable|excerpt_html({separator: 'excerpt|break', capture: 'before'}) }}\n{{ variable|excerpt_html({capture: 'after'}) }}\nhighlight\nHighlights a code string with highlight.php.\n{{ code|highlight(language) }}\nExamples:\n{{ '&lt;?php echo $highlighted-&gt;value; ?&gt;'|highlight('php') }}\npreg_split\nSplits a string into an array using a regular expression.\n{{ string|preg_split(pattern, limit) }}\nExample:\n{% set headers = page.content|preg_split('\/&lt;br[^&gt;]*&gt;\/') %}\npreg_match_all\nPerforms a regular expression match and return the group for all matches.\n{{ string|preg_match_all(pattern, group) }}\nExample:\n{% set tags = page.content|preg_match_all('\/&lt;[^&gt;]+&gt;(.*)&lt;\\\/[^&gt;]+&gt;\/') %}\nhex_to_rgb\nConverts a hexadecimal color to RGB.\n{{ color|hex_to_rgb }}",
      "content_html": "<h1>Filters</h1>\n<p>Variables can be modified by <a href=\"https://twig.symfony.com/doc/filters/index.html\" target=\"_blank\" rel=\"noopener noreferrer\">filters</a>. Filters are separated from the variable by a pipe symbol (<code translate=\"no\">|</code>). Multiple filters can be chained. The output of one filter is applied to the next.</p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ page.title|truncate(25)|<span class=\"hljs-keyword\">capitalize</span> }}</span></code></pre>\n<h2 id=\"filter-by\">filter_by</h2>\n<p>Filters a pages collection by variable name/value.</p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ collection|filter_by(variable, value) }}</span></code></pre>\n<p><em>Example:</em></p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ pages|filter_by('section', 'blog') }}</span></code></pre>\n<h2 id=\"filter\">filter</h2>\n<p>For more complex cases, you should use <a href=\"https://twig.symfony.com/doc/filters/filter.html\" target=\"_blank\" rel=\"noopener noreferrer\">Twig’s native <code translate=\"no\">filter</code></a>.</p>\n<p><em>Example:</em></p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-tag\">{% <span class=\"hljs-name\">pages</span>|<span class=\"hljs-keyword\">filter</span>(p =&gt; p.virtual == false and p.id not in ['page-1', 'page-2']) %}</span></code></pre>\n<h2 id=\"markdown-to-html\">markdown_to_html</h2>\n<p>Converts a Markdown string to HTML.</p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ markdown|markdown_to_html }}</span></code></pre>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-tag\">{% <span class=\"hljs-name\"><span class=\"hljs-keyword\">apply</span></span> markdown_to_html %}</span><span class=\"xml\">\n</span><span class=\"hljs-comment\">{# Markdown here #}</span><span class=\"xml\">\n</span><span class=\"hljs-template-tag\">{% <span class=\"hljs-name\"><span class=\"hljs-keyword\">endapply</span></span> %}</span></code></pre>\n<p><em>Examples:</em></p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-tag\">{% <span class=\"hljs-name\"><span class=\"hljs-keyword\">set</span></span> markdown = '**This is bold text**' %}</span><span class=\"xml\">\n</span><span class=\"hljs-template-variable\">{{ markdown|markdown_to_html }}</span></code></pre>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-tag\">{% <span class=\"hljs-name\"><span class=\"hljs-keyword\">apply</span></span> markdown_to_html %}</span><span class=\"xml\">\n**This is bold text**\n</span><span class=\"hljs-template-tag\">{% <span class=\"hljs-name\"><span class=\"hljs-keyword\">endapply</span></span> %}</span></code></pre>\n<h2 id=\"toc\">toc</h2>\n<p>Extracts only headings matching the given <code translate=\"no\">selectors</code> (h2, h3, etc.), or those defined in config <code translate=\"no\">pages.body.toc</code> if not specified.<br>\nThe <code translate=\"no\">format</code> parameter defines the output format: <code translate=\"no\">html</code> or <code translate=\"no\">json</code>.<br>\nThe <code translate=\"no\">url</code> parameter is used to build links to headings.</p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ markdown|toc(format, selectors, url) }}</span></code></pre>\n<p><em>Examples:</em></p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ page.body|toc }}</span><span class=\"xml\">\n</span><span class=\"hljs-template-variable\">{{ page.body|toc('html') }}</span><span class=\"xml\">\n</span><span class=\"hljs-template-variable\">{{ page.body|toc(selectors=['h2']) }}</span><span class=\"xml\">\n</span><span class=\"hljs-template-variable\">{{ page.body|toc(url=url(page)) }}</span></code></pre>\n<h2 id=\"json-decode\">json_decode</h2>\n<p>Converts a JSON string to an array.</p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ json|json_decode }}</span></code></pre>\n<p><em>Example:</em></p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-tag\">{% <span class=\"hljs-name\"><span class=\"hljs-keyword\">set</span></span> json = '{\"foo\": \"bar\"}' %}</span><span class=\"xml\">\n</span><span class=\"hljs-template-tag\">{% <span class=\"hljs-name\"><span class=\"hljs-keyword\">set</span></span> array = json|json_decode %}</span><span class=\"xml\">\n</span><span class=\"hljs-template-variable\">{{ array.foo }}</span></code></pre>\n<h2 id=\"yaml-parse\">yaml_parse</h2>\n<p>Converts a YAML string to an array.</p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ yaml|yaml_parse }}</span></code></pre>\n<p><em>Example:</em></p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-tag\">{% <span class=\"hljs-name\"><span class=\"hljs-keyword\">set</span></span> yaml = 'key: value' %}</span><span class=\"xml\">\n</span><span class=\"hljs-template-tag\">{% <span class=\"hljs-name\"><span class=\"hljs-keyword\">set</span></span> array = yaml|yaml_parse %}</span><span class=\"xml\">\n</span><span class=\"hljs-template-variable\">{{ array.key }}</span></code></pre>\n<h2 id=\"slugify\">slugify</h2>\n<p>Converts a string to a slug.</p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ string|slugify }}</span></code></pre>\n<h2 id=\"u\">u</h2>\n<p>The <code translate=\"no\">u</code> filter wraps a text in a Unicode object (a <a href=\"https://symfony.com/doc/current/components/string.html\" target=\"_blank\" rel=\"noopener noreferrer\">Symfony UnicodeString instance</a>) that exposes methods to \"manipulate\" the string.</p>\n<p><em>Example:</em></p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ 'cecil_string with twig'|u.camel.title }}</span></code></pre>\n<blockquote>\n<p>CecilStringWithTwig</p>\n</blockquote>\n<h2 id=\"singular\">singular</h2>\n<p>The <code translate=\"no\">singular</code> filter transforms a given noun in its plural form into its singular version.</p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ string|singular(locale)}}</span></code></pre>\n<p><em>Example:</em></p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-comment\">{# English (en) rules are used by default #}</span><span class=\"xml\">\n</span><span class=\"hljs-template-variable\">{{ 'partitions'|singular }}</span></code></pre>\n<blockquote>\n<p>partition</p>\n</blockquote>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ 'partitions'|singular('fr') }}</span></code></pre>\n<blockquote>\n<p>partition</p>\n</blockquote>\n<h2 id=\"plural\">plural</h2>\n<p>The <code translate=\"no\">plural</code> filter transforms a given noun in its singular form into its plural version.</p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ string|plural(locale)}}</span></code></pre>\n<p><em>Example:</em></p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-comment\">{# English (en) rules are used by default #}</span><span class=\"xml\">\n</span><span class=\"hljs-template-variable\">{{ 'animal'|plural }}</span></code></pre>\n<blockquote>\n<p>animals</p>\n</blockquote>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ 'animal'|plural('fr') }}</span></code></pre>\n<blockquote>\n<p>animaux</p>\n</blockquote>\n<h2 id=\"excerpt\">excerpt</h2>\n<p>Truncates a string and appends suffix.</p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ string|excerpt(length, suffix) }}</span></code></pre>\n<table>\n<thead>\n<tr>\n<th>Option</th>\n<th>Description</th>\n<th>Type</th>\n<th>Default</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>length</td>\n<td>Truncates after this number of characters.</td>\n<td>integer</td>\n<td>450</td>\n</tr>\n<tr>\n<td>suffix</td>\n<td>Appends characters.</td>\n<td>string</td>\n<td><code translate=\"no\">…</code></td>\n</tr>\n</tbody>\n</table>\n<p><em>Examples:</em></p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ variable|excerpt }}</span><span class=\"xml\">\n</span><span class=\"hljs-template-variable\">{{ variable|excerpt(250, '...') }}</span></code></pre>\n<h2 id=\"excerpt-html\">excerpt_html</h2>\n<p>Reads characters before or after <code translate=\"no\">&lt;!-- excerpt --&gt;</code> or <code translate=\"no\">&lt;!-- break --&gt;</code> tag.<br>\nSee <a href=\"../../content/7-markdown.md#excerpt\">Content documentation</a> for details.</p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ string|excerpt_html({separator, capture}) }}</span></code></pre>\n<table>\n<thead>\n<tr>\n<th>Option</th>\n<th>Description</th>\n<th>Type</th>\n<th>Default</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>separator</td>\n<td>String to use as separator.</td>\n<td>string</td>\n<td><code translate=\"no\">excerpt|break</code></td>\n</tr>\n<tr>\n<td>capture</td>\n<td>Part to capture, <code translate=\"no\">before</code> or <code translate=\"no\">after</code> the separator.</td>\n<td>string</td>\n<td><code translate=\"no\">before</code></td>\n</tr>\n</tbody>\n</table>\n<p><em>Examples:</em></p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ variable|excerpt_html }}</span><span class=\"xml\">\n</span><span class=\"hljs-template-variable\">{{ variable|excerpt_html({separator: 'excerpt|break', capture: 'before'}) }}</span><span class=\"xml\">\n</span><span class=\"hljs-template-variable\">{{ variable|excerpt_html({capture: 'after'}) }}</span></code></pre>\n<h2 id=\"highlight\">highlight</h2>\n<p>Highlights a code string with <a href=\"https://github.com/scrivo/highlight.php\" target=\"_blank\" rel=\"noopener noreferrer\">highlight.php</a>.</p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ code|highlight(language) }}</span></code></pre>\n<p><em>Examples:</em></p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ '&lt;?php echo $highlighted-&gt;value; ?&gt;'|highlight('php') }}</span></code></pre>\n<h2 id=\"preg-split\">preg_split</h2>\n<p>Splits a string into an array using a regular expression.</p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ string|preg_split(pattern, limit) }}</span></code></pre>\n<p><em>Example:</em></p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-tag\">{% <span class=\"hljs-name\"><span class=\"hljs-keyword\">set</span></span> headers = page.content|preg_split('/&lt;br[^&gt;]*&gt;/') %}</span></code></pre>\n<h2 id=\"preg-match-all\">preg_match_all</h2>\n<p>Performs a regular expression match and return the group for all matches.</p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ string|preg_match_all(pattern, group) }}</span></code></pre>\n<p><em>Example:</em></p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-tag\">{% <span class=\"hljs-name\"><span class=\"hljs-keyword\">set</span></span> tags = page.content|preg_match_all('/&lt;[^&gt;]+&gt;(.*)&lt;\\/[^&gt;]+&gt;/') %}</span></code></pre>\n<h2 id=\"hex-to-rgb\">hex_to_rgb</h2>\n<p>Converts a hexadecimal color to RGB.</p>\n<pre><code class=\"language-twig hljs twig\" translate=\"no\"><span class=\"hljs-template-variable\">{{ color|hex_to_rgb }}</span></code></pre>",
      "language": "en"
    }
  ]
}
