> ## 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.

# Theme Filters and Globals

> The SellAuth-specific Nunjucks filters and global functions available in themes and email templates.

## Overview

On top of the [standard Nunjucks filters](/developers/nunjucks#filters), SellAuth adds its own. Storefront themes and custom email templates get different sets, so each is listed separately below.

## Theme filters

Available in every `.njk` file of a storefront theme.

### URLs and assets

| Filter | Description |
| - | - |
| `assetUrl` | URL for a file in your theme's `assets/` folder, with a cache-busting version parameter. |
| `staticAsset(folder)` | URL for a library hosted by SellAuth. `folder` defaults to `themes`. |
| `shopUrl` | Turns a path into an absolute shop URL. Empty input becomes `#`, and a value that is already a full URL is passed through unchanged. |
| `imageUrl(default)` | Resolves an image ID from an `image` setting into its URL. |
| `apiInternalUrl` | Builds an absolute URL against the SellAuth API, for JavaScript that needs to call it. |

```njk theme={null}
<link href="{{ 'built.css' | assetUrl }}" rel="stylesheet" />
<script src="{{ 'splide@4.1.4.min.js' | staticAsset }}"></script>

<a href="{{ properties.link | shopUrl }}">{{ properties.text }}</a>
<img src="{{ properties.background_image | imageUrl }}" alt="" />
```

`shopUrl` is what makes links work correctly in both the live shop and the visual editor preview, so use it for every internal link rather than hardcoding a path.

### Dates and numbers

| Filter | Description |
| - | - |
| `formatDate` | Formats a date as `05 Jan 2026`. |
| `formatDateTime` | Same, with `hh:mm` appended. |
| `currentYear` | The current year. Ignores its input, so call it on an empty string. |
| `isNumber` | `true` if the value is a finite number. |

```njk theme={null}
<time datetime="{{ post.created_at }}">{{ post.created_at | formatDate }}</time>
<p>Copyright {{ shop.name }} {{ '' | currentYear }}</p>
```

### Colors

| Filter | Description |
| - | - |
| `hex_to_rgb` | Converts `#RRGGBB` or `#RRGGBBAA` into comma-separated channels, for use with `rgb()` and `rgba()` in CSS custom properties. |
| `themeColor` | The theme's accent color, falling back through the older `theme_color` setting and then to `#000000`. |

```njk theme={null}
<style>
  :root {
    --cl-accent: {{ (global.properties.accent_color if global.properties.accent_color else '#2B5FE3') | hex_to_rgb }};
  }
</style>

<meta name="theme-color" content="{{ '' | themeColor }}" />
```

`hex_to_rgb` expects a string. Guard it with a fallback as shown, because a setting that has never been saved is `null` and will throw.

### Text and content

| Filter | Description |
| - | - |
| `renderString` | Evaluates the Nunjucks placeholders inside a setting value, so a merchant can write things like `Welcome to {{ shop.name }}` in a text field. |
| `markup` | Escapes HTML, then turns `*text*` into `<span class="mk">text</span>` for highlighted words in headings. |
| `ytEmbedVideoId` | Extracts the video ID from any YouTube URL format. Returns the input unchanged if it does not match. |
| `readingTime(wpm)` | Whole minutes to read an HTML body, rounded up. Strips tags and entities first, and counts CJK characters at 500 per minute. `wpm` defaults to 225. Returns `0` for empty input. |

```njk theme={null}
<h2 class="sa-section-title">{{ properties.title | renderString | markup }}</h2>
<div class="sa-text-block-body">{{ properties.text | renderString }}</div>
```

Apply `renderString` to any free-text setting a merchant might want to interpolate into. Applying it to platform data instead of settings has no useful effect.

`readingTime` returns a number, so the surrounding copy stays yours to translate:

```njk theme={null}
{% set minutes = blogPost.content | readingTime %}
{% if minutes %}<span>{{ 'blog.reading_time' | t({ minutes: minutes }) }}</span>{% endif %}
```

It only works where the full body is in the render context. `blogPost` on the blog post page carries `content`; the entries in `blog_posts`, `latest_blog_posts`, `related_posts`, `prev_post` and `next_post` do not, so it returns `0` there.

### Data

| Filter | Description |
| - | - |
| `json(spaces)` | JSON-stringifies a value for use inside an inline `<script>` tag. The optional `spaces` argument pretty-prints. |

```njk theme={null}
<script>
  window.shopId = {{ shop.id | json }};
  window.defaultCurrency = {{ currency | json }};
</script>
```

<Warning>
  `json` is safe inside a `<script>` block only. Do not use it in an HTML attribute or in body text. Use the built-in `dump` filter there instead.
</Warning>

## Theme globals

Global functions are called directly rather than piped.

### formatPrice

```
formatPrice(price, currency = 'USD', locale = 'en-US')
```

Formats a number as a currency string with the correct symbol. Falls back to `<price> <currency>` if the currency code is not recognized.

```njk theme={null}
<span class="sa-product-card-price">
  {{ formatPrice(product.min_price, product.currency) }}
</span>
```

### helpers

| Function | Description |
| - | - |
| `helpers.components.products.getItemsByIds(items, ids)` | Resolves a `products_and_groups` setting value against a list of items. Finds products nested inside groups as well as top-level ones. |
| `helpers.numbers.formatCompact(number, maximumSignificantDigits, locale)` | `12400` becomes `12.4K`. |
| `helpers.numbers.formatNumber(number, locale)` | Thousands separators. |
| `helpers.numbers.formatDecimal(number, fractionDigits, locale)` | Fixed decimal places. |
| `helpers.arrays.shuffle(array)` | Returns a shuffled copy. |
| `helpers.arrays.chunks(array, chunkSize)` | Splits into chunks of at most `chunkSize`. |

Handpicking products in a component:

```njk theme={null}
{% set picked = helpers.components.products.getItemsByIds(sortedItems, properties.ids) %}
{% for item in picked %}
  {% render_snippet "product-card.njk", product=item %}
{% endfor %}
```

## Email templates

Custom email templates get **no** custom filters. Only the standard Nunjucks filters and this helper set are available:

| Function | Description |
| - | - |
| `helpers.date.getYear()` | The current year. |
| `helpers.date.formatDateTime(dateString, locale)` | Formats a date and time. `locale` defaults to `en-US`. Returns an empty string for a missing input. |
| `helpers.price.format(price, currency, locale)` | Formats a currency amount. Returns an empty string if either the price or the currency is missing. |

```njk theme={null}
<p>Total: {{ helpers.price.format(invoice.total, invoice.currency) }}</p>
<p>Placed {{ helpers.date.formatDateTime(invoice.created_at) }}</p>
<footer>Copyright {{ shop.name }} {{ helpers.date.getYear() }}</footer>
```


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