> ## Documentation Index
> Fetch the complete documentation index at: https://docs.sellauth.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Nunjucks Templating

> The template language used by SellAuth storefront themes and custom email templates.

## Overview

SellAuth renders templates with [Nunjucks](https://mozilla.github.io/nunjucks/), a Jinja-style template language for JavaScript. It is used in two places:

* **Storefront themes**, in every `.njk` file of a theme.
* **Custom email templates**, which use the same language with a different set of variables and helpers.

This page covers the language itself, then the parts specific to each. For the SellAuth-specific filters and functions, see [Filters and Globals](/developers/theme-filters).

## Language basics

### Output

```njk theme={null}
{{ shop.name }}
{{ product.min_price }}
{{ user.profile.display_name }}
```

Missing values render as an empty string rather than erroring, so a typo in a variable name fails silently.

### Conditionals

```njk theme={null}
{% if product.stock > 0 %}
  In stock
{% elif product.stock == 0 %}
  Sold out
{% else %}
  Unavailable
{% endif %}
```

There is also an inline form, which is how almost every setting fallback in the official themes is written:

```njk theme={null}
{{ properties.title if properties.title else 'Featured' }}
<div class="{{ 'is-centered' if properties.centered }}">
```

### Loops

```njk theme={null}
{% for item in items %}
  <a href="/product/{{ item.path }}">{{ item.name }}</a>
{% else %}
  <p>Nothing here yet.</p>
{% endfor %}
```

Inside a loop you get `loop.index` (1-based), `loop.index0`, `loop.first`, `loop.last`, and `loop.length`. Objects can be iterated as key and value pairs:

```njk theme={null}
{% for code, rate in currency_rates_usd %}
  <option value="{{ code }}">{{ code | upper }}</option>
{% endfor %}
```

### Variables

```njk theme={null}
{% set columns = properties.columns if properties.columns else 3 %}
```

The block form captures rendered output into a variable:

```njk theme={null}
{% set label %}
  {{ product.name }} ({{ formatPrice(product.min_price, product.currency) }})
{% endset %}
```

<Warning>
  A `{% set %}` inside a `{% for %}` or `{% if %}` block does not leak out of it. If you need a value after the block, set it before and reassign inside.
</Warning>

### Macros

Macros are reusable markup within a single file. They are file-local, you cannot call a macro defined in another file.

```njk theme={null}
{% macro priceTag(product) %}
  <span class="price">{{ formatPrice(product.min_price, product.currency) }}</span>
{% endmacro %}

{{ priceTag(product) }}
```

To share markup across files, use a snippet instead.

### Comments

```njk theme={null}
{# This is not rendered #}
```

Use these rather than HTML comments for anything a visitor should not see. HTML comments end up in the response.

### Filters

Filters transform a value with `|` and can be chained and given arguments:

```njk theme={null}
{{ post.created_at | formatDate }}
{{ properties.title | renderString | markup }}
{{ description | truncate(160) }}
```

Built-in filters worth knowing:

| Filter | Effect |
| - | - |
| `safe` | Marks a string as trusted HTML so it is not escaped |
| `escape` | Escapes HTML entities explicitly |
| `default(value)` | Substitutes a value when the input is undefined |
| `length` | Length of a string, array, or object |
| `join(sep)` | Joins an array into a string |
| `first`, `last` | First or last element |
| `lower`, `upper`, `title`, `capitalize` | Case changes |
| `replace(from, to)` | String replace |
| `truncate(n)` | Truncates to `n` characters |
| `round`, `abs` | Number helpers |
| `striptags` | Removes HTML tags |
| `urlencode` | Percent-encodes for use in a URL |
| `dump` | JSON representation, useful while debugging |

### The dictionary switch

Nunjucks has no `switch`. The official themes map a setting value to a class or a token with a dictionary lookup plus a fallback, and it is worth adopting because it keeps components readable:

```njk theme={null}
{% set widthClass = {'narrow': 'max-w-xl', 'default': 'max-w-3xl', 'wide': 'max-w-5xl'}[properties.max_width] or 'max-w-3xl' %}
```

## Escaping

Autoescaping is on. Everything you output with `{{ }}` is HTML-escaped unless you explicitly mark it safe.

```njk theme={null}
{{ shop.description }}          {# escaped, safe #}
{{ shop.description | safe }}   {# raw HTML, only do this deliberately #}
```

<Warning>
  Use `| safe` only on values you generate or that the platform documents as HTML. Never apply it to free-text a merchant or customer typed in. That is how a stored cross-site scripting hole gets built.
</Warning>

## Storefront themes

### Rendering components and snippets

Themes compose pages with two SellAuth-specific tags.

```njk theme={null}
{# by instance ID, from components_order #}
{% render_component componentId %}

{# by type, for global components rendered from the layout #}
{% render_component "footer" %}

{# snippets take keyword arguments, and the .njk extension is required #}
{% render_snippet "product-card.njk", product=item, card_style='flat' %}
```

If the target file does not exist, the literal text `Component not found` or `Snippet not found` is rendered in its place. The page still loads, which makes this easy to miss.

### Scope rules

The two tags scope differently, and this catches people out.

**Components get a fresh context.** A component receives its own `properties`, `componentId`, the page data, and `global`. It does **not** see variables you set in the file that rendered it.

```njk theme={null}
{% set highlight = 'blue' %}
{% render_component "hero" %}   {# hero cannot read `highlight` #}
```

Pass configuration to a component through its `properties` in `schema.json`, not through template variables.

**Snippet arguments are merged into the calling context and stay set.** After a `render_snippet` call, its arguments are still in scope for whatever comes next in the same file.

```njk theme={null}
{% render_snippet "section-header.njk", header_title="Products" %}
{% render_snippet "section-header.njk" %}   {# still sees header_title="Products" #}
```

Two conventions handle this, both used throughout the official themes:

1. **Prefix snippet arguments** with the snippet's name, for example `header_title`, `header_align`, so they cannot collide with anything else.
2. **Pass every argument explicitly** on every call, including the ones you want empty, rather than relying on them being unset.

Document the arguments in a comment at the top of the snippet:

```njk snippets/section-header.njk theme={null}
{#
  Shared section header.
  Args: header_title, header_headline (eyebrow), header_subtitle,
        header_align ('left'|'center'|'right', default 'center')
#}
```

## Email templates

Custom email templates use the same Nunjucks language, with two differences.

They are structured as named blocks rather than a layout plus a body:

```njk theme={null}
{% block subject %}Your order from {{ shop.name }}{% endblock %}

{% block content %}
  <p>Hi {{ customer.email }},</p>
  <p>Your order is ready.</p>
{% endblock %}
```

And they have a different helper set. Email templates get **no** SellAuth theme filters. They have `helpers.date` and `helpers.price` only, listed in [Filters and Globals](/developers/theme-filters#email-templates).

See [Email Templates](/developers/email-templates) for the templates you can override, the variables each one receives, and how the shared layout works.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.