Documentation
STE Template Engine Filters
STE provides a rich set of built-in filters to transform and format data in your templates. Filters are applied using the pipe syntax: {{ value | filterName }}
or with arguments: {{ value | filterName(arg1, arg2) }}
.
Available Filters
abs
Converts a number to its absolute value.
{{ -42 | abs }} → 42
capitalize
Capitalizes the first character of a string.
{{ "hello world" | capitalize }} → "Hello world"
chunk
Splits an array into chunks (sub-arrays) each of the specified size. Useful for creating grids, pagination, or batching operations.
Basic Usage:
{{ [1,2,3,4,5] | chunk(2) }}
// Outputs: [[1, 2], [3, 4], [5]]
Practical Examples:
<!-- Create a 3-column grid layout -->
{{#set productRows = products | chunk(3)}}
<!-- Iterate over chunks -->
{{#each productRows}}
<div class="row">
{{#each1 this}}
<div class="col-4">{{this.name}}</div>
{{/each1}}
</div>
{{/each}}
<!-- Process items in batches -->
{{#set batches = items | chunk(10)}}
<!-- Iterate over chunks -->
{{#each batches}}
<div class="batch">Processing {{this | length}} items...</div>
{{/each}}
Note: The last chunk may contain fewer elements if the array length is not evenly divisible by the chunk size.
currency
Formats a number as currency with an optional symbol (defaults to $).
{{ 42.5 | currency }} → "42.50 $"
{{ 42.5 | currency("€") }} → "42.50 €"
cycle
Creates a cycle through a set of values, useful for alternating classes or values.
Alternates between "odd" and "even" with each call
{{#set altClass = "alternate" | cycle("odd", "even")}}
{{#each articles}}
<div class="{{ altClass | next }}">{{ title }}</div>
{{/each}}
dateFormat
Formats a date using the specified format string with enhanced options.
{{ date | dateFormat }}
↪ "2024-02-14" (default format: YYYY-MM-DD)
{{ date | dateFormat("DD/MM/YYYY HH:mm") }}
↪ "14/02/2024 15:30" (custom date and time format)
{{ date | dateFormat("MMMM D, YYYY") }}
↪ "February 14, 2024" (month name format)
{{ date | dateFormat("MMM DD, YY") }}
↪ "Feb 14, 24" (abbreviated format)
{{ date | dateFormat("DDDD, HH:mm") }}
↪ "Wednesday, 15:30" (day of week format)
{{ date | dateFormat("h:mm a") }}
↪ "3:30 pm" (12-hour time format)
{{ date | dateFormat("YYYY-MM-DD", false) }}
↪ "2024-02-14" (using local timezone)
Tokens
A complete list of tokens supported by dateFormat
:
Token | Example Output | Description |
---|---|---|
YYYY / yyyy |
2024 |
Full year |
YY / yy |
24 |
Last two digits of the year |
MMMM / mmmm |
February |
Full month name |
MMM / mmm |
Feb |
Abbreviated month name |
MM |
02 |
Zero-padded month number |
M |
2 |
Numeric month number |
DD / dd |
09 |
Zero-padded day of month |
D / d |
9 |
Day of month |
dddd |
Wednesday |
Full weekday name |
ddd |
Wed |
Abbreviated weekday name |
HH / hh |
08 |
Zero-padded hour (24-hour format) |
H / h |
8 |
Hour (24-hour format) |
mm |
05 |
Zero-padded minutes |
m |
5 |
Minutes |
ss |
09 |
Zero-padded seconds |
s |
9 |
Seconds |
A |
AM / PM |
Uppercase meridiem |
a |
am / pm |
Lowercase meridiem |
Z |
+05:30 , +00:00 |
Timezone offset (if useUTC = false) |
The boolean optional parameter controls whether to use UTC (true) or local time (false), with UTC being the default.
Localization
An example for french:
const frenchLocale = {
monthNames: [
"janvier",
"février",
"mars",
"avril",
"mai",
"juin",
"juillet",
"août",
"septembre",
"octobre",
"novembre",
"décembre",
],
monthNamesShort: ["janv", "févr", "mars", "avr", "mai", "juin", "juil", "août", "sept", "oct", "nov", "déc"],
weekdayNames: ["dimanche", "lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi"],
weekdayNamesShort: ["dim", "lun", "mar", "mer", "jeu", "ven", "sam"],
}
// Using dateFormat with frenchLocale in a template
dateFormat("2024-02-14", "dddd D MMMM YYYY", false, frenchLocale)
// → "mercredi 14 février 2024"
defaults
Returns the first non-null value from a list of values.
{{ null | defaults(undefined, "", "fallback") }} → "fallback"
dump
Converts a value to a formatted JSON string for debugging.
{{ object | dump }} → Pretty-printed JSON representation
escape
Escapes HTML special characters in a string.
{{ "<div>" | escape }} → "<div>"
fileSize
Formats a number of bytes into a human-readable size.
{{ 1024 | fileSize }} → "1 KB"
{{ 1048576 | fileSize }} → "1 MB"
first
Returns the first element of an array or first character of a string.
{{ [1, 2, 3] | first }} → 1
{{ "hello" | first }} → "h"
groupBy
Groups an array of objects by a specified key. Supports nested properties using dot and bracket notation.
{{ users | groupBy("country") }}
↪ Objects grouped by country property
{{ posts | groupBy("frontmatter.category") }}
↪ Objects grouped by nested category
{{ data | groupBy("metadata['group-id']") }}
↪ Objects grouped by property with special characters
has
Checks if a value exists in an array, object, or string.
{{ object | has("property") }} → true/false
{{ "hello world" | has("world") }} → true
int
Converts a value to an integer.
{{ "42.9" | int }} → 42
join
Joins array elements with specified separators.
{{ ["cherry", "kiwi", "peach"] | join(", ") }} → "cherry, kiwi, peach"
{{ ["cherry", "kiwi", "peach"] | join(", ", " and ") }}
↪ "cherry, kiwi and peach"
last
Returns the last element of an array or last character of a string.
{{ [1, 2, 3] | last }} → 3
{{ "hello" | last }} → "o"
length
Returns the length of a string, array, or number of keys in an object.
{{ "hello" | length }} → 5
{{ [1, 2, 3] | length }} → 3
log
Logs a value to the console for debugging (returns the original value).
{{ value | log("Debug label") }} → Logs to console and returns value
lowercase
Converts a string to lowercase.
{{ "HELLO" | lowercase }} → "hello"
next
Used with cycle to get the next value in the cycle.
{{ cycleId | next }} → Gets next value in the previously defined cycle
numberFormat
Formats numbers with suffixes for large values.
{{ 1500000 | numberFormat }} → "1.5M"
preserveSpaces
Preserves all spaces in strings when rendered in HTML by converting them to
entities.
{{" Multiple Spaces Here " | preserveSpaces}}
range
Checks if a number is within a specified range.
{{ 5 | range(1, 10) }} → true
{{ 15 | range(1, 10) }} → false
{{ 20 | range(20, 30)}} → false
{{ 20 | range(20, 30, true)}} → true (inclusive)
removeSpaces
Removes all whitespace from a string.
{{ "hello world" | removeSpaces }} → "helloworld"
replace
Replaces all occurrences of a substring in a string.
{{ "hello world" | replace("world", "there") }} → "hello there"
reverse
Reverses a string or array.
{{ "hello" | reverse }} → "olleh"
{{ [1, 2, 3] | reverse }} → [3, 2, 1]
round
Rounds a number to specified decimal places.
{{ 3.14159 | round(2) }} → 3.14
safeStringify
Safely converts an object to JSON string, handling circular references.
{{ complexObject | safeStringify }} → JSON string with "[Circular]" for circular references
skip
Skips the first N elements if N is positive, or skips the last N elements if N is negative, and returns the remaining array. Useful for pagination or excluding certain items.
Basic Usage:
{{ [10, 20, 30, 40] | skip(2) }}
// Outputs: [30, 40]
{{ [10, 20, 30, 40] | skip(-2) }}
// Outputs: [10, 20]
Practical Examples:
<!-- Skip hero post, show the rest -->
{{#set regularPosts = posts | skip(1)}}
<!-- Iterate over posts -->
{{#each regularPosts}}
<article class="regular-post">{{this.title}}</article>
{{/each}}
<!-- Pagination: skip first 20 items for page 3 -->
{{#set page3Items = allItems | skip(20) | take(10)}}
<!-- Exclude admin and moderator roles -->
{{#set regularUsers = users | skip(-2)}}
<!-- Skip header and footer rows in data -->
{{#set dataRows = csvRows | skip(1) | skip(-1)}}
slice
Returns a portion of an array between start and end indices. Supports negative indices (like Python), counting from the end. Most flexible for extracting specific ranges.
Basic Usage:
{{ [10, 20, 30, 40, 50] | slice(1, 3) }}
// Outputs: [20, 30]
{{ [10, 20, 30, 40, 50] | slice(-3, -1) }}
// Outputs: [30, 40]
{{ [10, 20, 30, 40, 50] | slice(2) }}
// Outputs: [30, 40, 50] (from index 2 to end)
Practical Examples:
<!-- Get middle section of articles -->
{{#set middleArticles = articles | slice(2, -2)}}
<!-- Custom pagination -->
{{#set pageStart = (currentPage - 1) * itemsPerPage}}
<!---->
{{#set pageEnd = pageStart + itemsPerPage}}
<!---->
{{#set pageItems = allItems | slice(pageStart, pageEnd)}}
<!-- Get specific range for carousel -->
{{#set carouselItems = images | slice(0, 5)}}
<!-- Extract data without first and last elements -->
{{#set cleanData = rawData | slice(1, -1)}}
<!-- Time-based slicing (if array is date-sorted) -->
{{#set thisWeek = events | slice(-7)}}
Parameter Details:
start
(optional, default: 0): Starting index. Negative values count from end.end
(optional, default: array length): Ending index (exclusive). Negative values count from end.- If
start > end
, returns empty array. - If
end
is omitted, slices to the end of the array.
Common Patterns:
<!-- First half -->
{{#set itemsLength = items | length}}
<!---->
{{#set halfItems = itemsLength / 2}}
<!---->
{{#set firstHalf = items | slice(0, halfItems)}}
<!-- Last 3 items -->
{{#set lastThree = items | slice(-3)}}
<!-- Everything except first and last -->
{{#set middle = items | slice(1, -1)}}
Note: Negative indices count backwards from the end of the array. The slice operation does not modify the original array.
slugify
Converts a string to URL-friendly slug format.
{{ "Hello World!" | slugify }} → "hello-world"
sortBy
Sorts an array of objects by a specified key. Supports nested properties and custom sort order ('asc' by default, 'desc' for descending).
{{ users | sortBy("name") }}
↪ Array sorted by name ascending
{{ users | sortBy("name", "desc") }}
↪ Array sorted by name descending
{{ posts | sortBy("frontmatter.author.name") }}
↪ Array sorted by nested author name
{{ data | sortBy("metadata['priority-order']", "desc") }}
↪ Array sorted by property with special characters
sortByDate
Sorts an array of objects by a date field. Supports nested properties and sort order ('asc' by default, 'desc' for descending). Ensures proper date comparison.
{{ posts | sortByDate("date") }}
↪ Array sorted by date ascending
{{ posts | sortByDate("frontmatter.publish_date", "desc") }}
↪ Array sorted by nested date descending
{{ events | sortByDate("metadata['scheduled-at']") }}
↪ Array sorted by date with special characters
take
Returns the first N elements if N is positive, or the last N elements if N is negative. Perfect for "featured items" or "recent posts" sections.
Basic Usage:
{{ [10, 20, 30, 40] | take(2) }}
// Outputs: [10, 20]
{{ [10, 20, 30, 40] | take(-2) }}
// Outputs: [30, 40]
Practical Examples:
<!-- Show first 3 featured posts -->
{{#set featured = posts | take(3)}}
<!-- Iterate over featured -->
{{#each featured}}
<article class="featured-post">{{this.title}}</article>
{{/each}}
<!-- Show last 5 recent comments -->
{{#set recent = comments | take(-5)}}
<div class="recent-comments">
{{#each recent}}
<div class="comment">{{this.text}}</div>
{{/each}}
</div>
<!-- Combined with other filters -->
{{#set topRated = products | sortBy("rating", "desc") | take(4)}}
timeAgo
Converts a date to a relative time string.
{{ date | timeAgo }} → "2 hours ago"
toLink
Creates an HTML link with optional attributes for external links.
{{ "https://example.com" | toLink("Visit Site") }}
↪ '<a href="https://example.com">Visit Site</a>'
{{ "https://example.com" | toLink("Visit Site", true) }}
↪ '<a href="https://example.com" target="_blank">Visit Site</a>'
{{ "https://example.com" | toLink("Visit Site", true, true) }}
↪ '<a href="https://example.com" target="_blank" rel="external noopener noreferrer">Visit Site</a>'
trim
Removes whitespace from both ends of a string.
{{ " hello " | trim }} → "hello"
truncate
Truncates a string to specified length.
{{ "Long text here" | truncate(5) }} → "Long..."
truncateWords
Truncates a string to specified number of words.
{{ "The quick brown fox" | truncateWords(2) }} → "The quick..."
type
Returns the JavaScript type of a value.
{{ value | type }} → "string", "number", "object", etc.
uppercase
Converts a string to uppercase.
{{ "hello" | uppercase }} → "HELLO"
where
Filters an array of objects by matching a property value. Supports nested properties using dot and bracket notation.
{{ users | where("active", true) }}
↪ Array of users where active is true
{{ posts | where("frontmatter.published", true) }}
↪ Array of posts where nested published is true
{{ data | where("metadata['user-status']", "approved") }}
↪Array filtered by property with special characters
wordCount
Counts the number of words in a string.
{{ "hello world" | wordCount }} → 2
Filter Chaining
Filters can be chained together using multiple pipe operators. The output of each filter becomes the input for the next filter in the chain.
{{ "HELLO WORLD" | lowercase | capitalize }} → "Hello world"
{{ [1, 2, 3] | reverse | join(", ") }} → "3, 2, 1"
{{ user.name | defaults("Anonymous") | uppercase }}
↪ "ANONYMOUS" (if user.name is null)
{{ price | currency | escape }} → "$42.50"
Best Practices
Performance Considerations by Filter Type
High Performance Filters
These filters (slice, take, skip, chunk) are very efficient and safe to use with larger arrays:
- slice, take, skip: O(n) complexity, create shallow copies
- chunk: O(n) complexity, efficient memory usage
- Safe for arrays up to 10,000+ items
- Minimal memory overhead
Good uses:
- Paginating through 5,000 search results
- Taking first 100 items from a large dataset
- Chunking 2,000 products for batch processing
- Slicing large arrays for carousel displays
Moderate Performance Filters
These filters (groupBy, sortBy, where) are designed for moderate-sized arrays (typically < 1,000 items):
- sortBy: O(n log n) complexity
- groupBy, where: O(n) complexity but create new data structures
- Consider server-side alternatives for larger datasets
Good uses:
- Sorting a list of 50 blog categories
- Filtering a table with 200 rows for client-side search
- Grouping 100 items by category
Avoid:
- Sorting 10,000 database records
- Filtering large API responses
- Grouping entire data tables
Recommended Approaches by Data Size
Small Arrays (< 100 items)
<!-- All filters are safe and fast -->
{{#set sorted = items | sortBy("name") | take(10)}}
<!---->
{{#set grouped = items | groupBy("category") | slice(0, 5)}}
Medium Arrays (100-1,000 items)
<!-- Use pagination with slice/take -->
{{#set pageItems = items | slice(startIndex, endIndex)}}
<!---->
{{#set featured = items | take(5)}}
<!-- Be cautious with sortBy/groupBy -->
{{#set sorted = items | sortBy("priority") | take(20)}}
Large Arrays (1,000+ items)
<!-- Prefer slice/take/skip for performance -->
{{#set recentItems = items | take(-50)}}
<!---->
{{#set pageChunk = items | slice(pageStart, pageEnd)}}
<!-- Avoid heavy operations in templates -->
<!-- ❌ Don't do this: -->
{{#set heavySort = largeArray | sortBy("complex.nested.property")}}
<!-- ✅ Do this instead: Sort on server, then slice -->
{{#set pageData = preSortedArray | slice(offset, limit)}}
Filter Chaining Best Practices
Efficient Chaining Order
<!-- ✅ Good: Filter first, then sort smaller subset -->
{{#set result = items | where("status", "active") | sortBy("date") | take(10)}}
<!-- ❌ Inefficient: Sort everything, then filter -->
{{#set result = items | sortBy("date") | where("status", "active") | take(10)}}
Memory-Conscious Patterns
<!-- ✅ Good: Chain lightweight operations -->
{{#set result = items | skip(10) | take(20) | chunk(5)}}
<!-- ⚠️ Consider impact: Multiple heavy operations -->
{{#set result = items | sortBy("name") | groupBy("type") | sortBy("date")}}
Server-Side vs Client-Side Decision Matrix
Operation | Small Arrays | Medium Arrays | Large Arrays |
---|---|---|---|
Pagination | Either | Client (slice) | Server |
Sorting | Either | Client (sortBy) | Server |
Filtering | Either | Client (where) | Server |
Grouping | Either | Client (groupBy) | Server |
Taking/Skipping | Client | Client | Either |
Chunking | Client | Client | Client |
Memory Usage Guidelines
- slice/take/skip: Create shallow copies, minimal memory impact
- chunk: Creates nested arrays, moderate memory usage
- sortBy/groupBy: May create temporary data structures
- where: Creates filtered copy of array
Rule of thumb: If your filter operation would take >100ms to complete, consider moving it server-side.
Content
- Available Filters
- Filter Chaining
- Best Practices