> For the complete documentation index, see [llms.txt](https://docs.feedotter.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.feedotter.com/set-up-your-own-template/advanced-template-information/filters.md).

# Filters

Custom and built-in Twig filters available inside FeedOtter email templates. All tokens use \[\[ ]] for output and \[% %] for logic blocks.

**Basic syntax:**

```
[[ variable | filterName ]]
[[ variable | filterName(arg1, arg2) ]]
[[ variable | filterName(anotherVariable) ]]
```

***

### Working with Dates

FeedOtter can use any date format supported by PHP. Full reference: <https://php.net/manual/en/datetime.formats.date.php>

The most common date variable is `feedotter.send_date_object`, which represents the scheduled send date of the campaign.

```
[[feedotter.send_date_object | date('F j, Y')]]       // September 21, 2022
[[feedotter.send_date_object | date('F d')]]           // September 21
[[feedotter.send_date_object | date('M. j, Y')]]       // Sep. 21, 2022
[[feedotter.send_date_object | date('d-m-y')]]         // 21-09-22
[[feedotter.send_date_object | date('l, F j, Y')]]     // Wednesday, September 21, 2022
[[feedotter.send_date_object | date('D, F j, Y')]]     // Wed, Sep. 21, 2022
[[feedotter.send_date_object | date('F Y')]]           // September 2022
[[feedotter.send_date_object | date('n/j/Y')]]         // 9/21/2022
```

To render the date in a specific timezone, pass the timezone as a second argument to `date()`:

```
[[feedotter.send_date_object | date('g:i A T', 'America/Los_Angeles')]]  // 9:00 AM PDT
[[feedotter.send_date_object | date('F j, Y', 'America/Chicago')]]       // September 21, 2022
```

You can also convert the timezone first using `switch_timezone` and then format:

```
[[feedotter.send_date_object | switch_timezone('America/New_York') | date('l, F j, Y')]]
```

***

### Custom Filters

#### `linkColor`

Finds all `<a>` tags in an HTML string and injects an inline `color` style, overriding any existing color declaration. Passes through plain text (no HTML) unchanged.

| Argument | Type   | Description                                                     |
| -------- | ------ | --------------------------------------------------------------- |
| `color`  | string | Hex color value. Accepts `#RRGGBB`, `RRGGBB`, `#RGB`, or `RGB`. |

```
[[ custom.body | linkColor('#c0392b') ]]
[[ custom.body | linkColor(custom.linkColor) ]]
```

Output on each anchor: `style="color: #c0392b !important;"`

> If the `<a>` tag already has a `style` attribute, the new color is appended and any prior `color` declaration is removed.

***

#### `resize`

Rewrites an image URL to pass through the FeedOtter resize proxy at the specified dimensions.

| Argument  | Type   | Required | Description                                      |
| --------- | ------ | -------- | ------------------------------------------------ |
| `width`   | int    | Yes      | Target width in pixels.                          |
| `height`  | int    | No       | Target height in pixels.                         |
| `overlay` | string | No       | Overlay identifier passed to the resize service. |

```
[[ post.image_url | resize(500) ]]
[[ post.image_url | resize(500, 300) ]]
```

***

#### `cleanImages`

Strips `width` and `height` attributes (both HTML attributes and inline style values) from all `<img>` tags, then adds `width="100%"` to any image that has no width set. Useful when displaying raw HTML content in an email.

```
[[ post.post_content | cleanImages ]]
```

**Common use case — rendering raw CMS HTML:**

```
[[ post.post_content | striptags('<p><br><span><a><div><b><strong><i><em><img>') | cleanImages ]]
```

***

#### `regex_replace`

Runs a PHP `preg_replace` on a string. Gives full control over substitution using regular expressions.

| Argument      | Type   | Required | Description                                                 |
| ------------- | ------ | -------- | ----------------------------------------------------------- |
| `pattern`     | string | Yes      | A PCRE regex pattern (including delimiters, e.g. `/foo/i`). |
| `replacement` | string | Yes      | Replacement string (supports backreferences like `$1`).     |
| `limit`       | int    | No       | Max replacements. Default `-1` (all).                       |

```
[[ post.post_title | regex_replace('/\\s+/', '-') ]]
```

**Common use case — strip Yoast/plugin-inserted footer text:**

```
[[ post.post_content | regex_replace('/The post .* (?:appeared first|first appeared) .*\./', '') ]]
```

***

#### `intl_date`

Formats a `DateTime` object using the ICU pattern format via PHP's `IntlDateFormatter`. Useful for localized date strings.

| Argument  | Type   | Default                       | Description                                     |
| --------- | ------ | ----------------------------- | ----------------------------------------------- |
| `locale`  | string | `es_ES`                       | A BCP 47 locale string (e.g. `en_US`, `fr_FR`). |
| `pattern` | string | `EEEE d, 'de' MMMM 'de' yyyy` | ICU date pattern.                               |

```
[[ feedotter.send_date_object | intl_date('en_US', 'MMMM d, yyyy') ]]
[[ feedotter.send_date_object | intl_date ]]
```

***

#### `switch_timezone`

Converts a `DateTime` object to a different timezone and returns the adjusted `DateTime`. Typically chained before a `date` format filter.

| Argument   | Type   | Description                                               |
| ---------- | ------ | --------------------------------------------------------- |
| `timezone` | string | A valid PHP timezone identifier (e.g. `America/Chicago`). |

```
[[ feedotter.send_date_object | switch_timezone('America/New_York') | date('F j, Y') ]]
```

***

#### `splitsentences`

Splits a block of text into an array of individual sentences. Sentences shorter than 4 characters are discarded. Typically used with a `[% for %]` loop.

```
[% for sentence in post.post_content | splitsentences %]
  <p>[[ sentence ]]</p>
[% endfor %]
```

***

#### `shuffle`

Randomly shuffles an array of posts or items. Each render produces a different order.

```
[% set posts = posts | shuffle %]
```

***

#### `marker`

Tags an array of posts with a named marker (recording their URLs internally) and returns the array unchanged. Used by FeedOtter for deduplication tracking.

| Argument     | Type   | Description                                 |
| ------------ | ------ | ------------------------------------------- |
| `markerName` | string | An arbitrary label for this group of posts. |

```
[% set featured = posts | marker('featured') %]
```

***

#### `kebab`

Converts a string to lowercase kebab-case, stripping non-alphanumeric characters and replacing spaces with hyphens.

```
[[ post.post_title | kebab ]]
[[ "My Article Title" | kebab ]]  {# → my-article-title #}
```

***

### Commonly Used Built-in Filters

These standard Twig filters are also available in FeedOtter templates.

| Filter                                  | Description                                        | Example                                                      |
| --------------------------------------- | -------------------------------------------------- | ------------------------------------------------------------ |
| `date(format)`                          | Format a date/DateTime.                            | `[[ feedotter.send_date_object \| date('F Y') ]]`            |
| `truncate(length, preserve, separator)` | Truncate plain text.                               | `[[ post.post_excerpt_text \| truncate(200, true, '...') ]]` |
| `upper`                                 | Uppercase a string.                                | `[[ post.post_title \| upper ]]`                             |
| `lower`                                 | Lowercase a string.                                | `[[ post.post_title \| lower ]]`                             |
| `capitalize`                            | Capitalize the first character.                    | `[[ post.post_title \| capitalize ]]`                        |
| `title`                                 | Title-case every word.                             | `[[ post.post_title \| title ]]`                             |
| `trim`                                  | Strip leading/trailing whitespace.                 | `[[ custom.field \| trim ]]`                                 |
| `nl2br`                                 | Convert newlines to `<br>` tags.                   | `[[ custom.bio \| nl2br ]]`                                  |
| `replace({'old':'new'})`                | Find-and-replace within a string.                  | `[[ post.post_title \| replace({'&amp;': 'and'}) ]]`         |
| `default(fallback)`                     | Return a fallback if the value is empty.           | `[[ custom.headline \| default('Read More') ]]`              |
| `length`                                | Count characters in a string or items in an array. | `[% if post.post_title \| length > 60 %]`                    |
| `slice(start, length)`                  | Return a portion of a string or array.             | `[[ posts \| slice(0, 3) ]]`                                 |
| `join(glue)`                            | Join an array into a string.                       | `[[ tags \| join(', ') ]]`                                   |
| `first`                                 | First item of an array.                            | `[[ posts \| first ]]`                                       |
| `last`                                  | Last item of an array.                             | `[[ posts \| last ]]`                                        |
| `reverse`                               | Reverse a string or array.                         | `[[ posts \| reverse ]]`                                     |
| `sort`                                  | Sort an array.                                     | `[[ tags \| sort ]]`                                         |
| `url_encode`                            | URL-encode a string.                               | `[[ post.post_url \| url_encode ]]`                          |
| `json_encode`                           | Encode a value as JSON.                            | `[[ data \| json_encode ]]`                                  |

#### `striptags`

Strips HTML tags from a string. When called with no argument, all tags are removed. Pass an allowlist of tags to keep specific ones — useful when you need to render CMS HTML in email without carrying over layout-heavy or unknown markup.

```
{{-- Remove all tags (plain text only) --}}
[[ post.post_content | striptags ]]

{{-- Keep only email-safe inline/block tags --}}
[[ post.post_content | striptags('<p><br><span><a><div><b><strong><i><em><img>') ]]
```

> **Note:** Even with an allowlist, CMS content can still carry unsafe `style` attributes or deeply nested structures. Pair with `cleanImages` and `regex_replace` to further sanitize.

**Full cleanup chain for CMS HTML content:**

```
[[ post.post_content
    | regex_replace('/The post .* (?:appeared first|first appeared) .*\./', '')
    | striptags('<p><br><span><a><div><b><strong><i><em><img>')
    | cleanImages ]]
```

***

### Rarely Used / Specialist Filters

These filters are available but not commonly needed in standard templates.

#### `truncate_html`

Truncates an HTML string to approximately `length` plain-text characters, preferring a sentence boundary, then a word boundary. Automatically closes any tags left open by the cut.

| Argument | Type | Default | Description                              |
| -------- | ---- | ------- | ---------------------------------------- |
| `length` | int  | `100`   | Maximum number of plain-text characters. |

```
[[ post.post_content | truncate_html(300) ]]
```

***

#### `truncate_sentence`

Truncates a **plain-text** string to `length` characters, snapping back to the nearest sentence-ending punctuation (`.`, `?`, or `!`) so the result never ends mid-sentence.

| Argument | Type | Description                                                     |
| -------- | ---- | --------------------------------------------------------------- |
| `length` | int  | Maximum character count before snapping to a sentence boundary. |

```
[[ post.post_excerpt_text | truncate_sentence(200) ]]
```

***

#### `resize2`

Alternative resize filter backed by Cloudinary. Resizes to the given width (and optional height) using a fill crop. Returns a Cloudinary fetch URL.

| Argument | Type | Required | Description              |
| -------- | ---- | -------- | ------------------------ |
| `width`  | int  | Yes      | Target width in pixels.  |
| `height` | int  | No       | Target height in pixels. |

```
[[ post.image_url | resize2(600, 400) ]]
```

***

#### `mktohash`

Generates a random alphanumeric string of a given length. Useful for Marketo hash tokens or unique identifiers.

| Argument | Type | Default | Description                       |
| -------- | ---- | ------- | --------------------------------- |
| `length` | int  | `12`    | Number of characters to generate. |

```
[[ 'seed' | mktohash(16) ]]
```

***

#### `foDebugJson`

Pretty-prints any value (object, array, or scalar) as an HTML-escaped JSON string. Intended for template debugging only — remove before sending.

```
[[ post | foDebugJson ]]
```

***

*Last updated: August 2026*
