Profile Picture of the author

Jinja in SnapApp

on 02-05-2026 12:00 AM by SnapApp by BlueVector AI

1135

License: Professional, Corporate, Enterprise

Jinja is a template engine for Python that helps you generate dynamic content inside HTML pages.
Instead of hard-coding values like names, addresses, or IDs, Jinja allows you to insert live data directly into your pages.

In SnapApp, Jinja is used to: - Display dynamic values from objects and records - Add conditions and loops inside pages - Reuse logic using functions and macros - Build flexible and reusable page layouts

This document explains Jinja step by step, starting from the basics and gradually moving to advanced features.
No prior Jinja experience is required.

For full syntax reference, you can also visit the official
Jinja Documentation.

Table of Contents


Delimiters in Jinja

Delimiters tell Jinja what should be processed as logic and what should be printed as output.
Anything outside these delimiters is treated as normal text or HTML.

Delimiter What it does Example
{{ ... }} Prints a value to the page {{ customer['full_name'] }}
{% ... %} Executes logic (no direct output) {% set x = 42 %}
{# ... #} Adds comments (not shown on page) {# This is a comment #}

Think of it this way: - {{ }}Show something - {% %}Do something - {# #}Explain something


Variables in Jinja

Variables are used to store values that can be reused later in the template.
You do not need to define a data type—Jinja automatically understands it.

{% set totalOrders = 150 %}
{{ totalOrders }}

Output: 150

Variables can also be conditionally defined, enabling more tailored content:

{% set displayName = user.first_name | title if user.first_name else "SnapApp Invalid User" %}

This Means :

  • IF user.first_name exists → Display user.first_name in Title Case

  • ELSE → Display a default message

Variable data types

Jinja supports a wide range of data types that are commonly used in modern web applications.
Understanding these data types helps you work confidently with variables, apply conditions correctly, and avoid runtime errors while building SnapApp templates.

Each variable in Jinja automatically assumes a data type based on the value assigned to it—there is no need for explicit type declarations.

Data Type What does it represent Example Values
None Represents the absence of any value. Often used when data is missing or undefined. none or None
Integer Represents whole numbers without decimal points. 100, 54, 3021
Float Represents numbers that contain decimal values. 12.34, 3.75, 42.5, 3.1415, 15.0
String Represents text or a sequence of characters. “status”, “SnapappUser”, “SnapApp is Cool”
Boolean Represents logical true or false values. true or True, false or False
List A mutable, ordered collection of elements enclosed in [ ]. Lists can contain mixed data types. [1, ‘string’, [ [ ], [ ] ], { 1: ‘a’ }, none ]
Tuple An immutable, ordered collection of elements enclosed in ( ). Once created, tuples cannot be modified. (1, ‘string’, [ [ ], [ ] ], { 1: ‘a’ }, none )
Dictionary A collection of key-value pairs enclosed in { }. Keys are unique, and values can be any data type. { ‘name’: ‘John’, ‘role’: ‘Admin’ }

Jinja allows conditional logic to be embedded directly within templates.
This enables dynamic content rendering based on the current state of variables, such as status values, user data, or configuration settings.

{% if project.status == 'pending' %}
  {{ "Project is pending." }}
{% elif project.status == 'deal qualified' %}
  {{ "Order has qualified the deal." }}
{% else %}
  {{ "Order is complete." }}
{% endif %}

In this example:

  • The if condition checks whether user.first_name exists.

  • The else block provides a fallback message when user.first_name is not available.

  • Only one block is rendered based on whether the condition evaluates to true.

Expressions

Expressions in Jinja allow you to perform calculations, compare values, and manipulate data directly inside templates.
They follow familiar Python-style syntax, making them easy to read and use, even for beginners.

Expressions are commonly used to: - Make decisions (for example, checking a status or condition) - Perform simple math - Combine or evaluate values dynamically - Control what content is rendered in SnapApp templates

a. Math Operators

Math operators are used to perform basic arithmetic operations on numeric values.

- `+` _Addition_  
  Adds two values together.
- `-` _Subtraction_  
  Subtracts one value from another.
- `*` _Multiplication_  
  Multiplies two values.
- `/` _Division_  
  Divides one value by another and returns a decimal result.
- `//` _Integer division, rounding down._  
  Divides two values and returns only the whole number part.  
  Example: `20 // 7` returns `2`.
- `%` _Modulus (remainder of division)._  
  Returns the remainder after division.  
  Example: `11 % 7` returns `4`.

b. Comparison Operators

Comparison operators are used to **compare two values** and return a boolean result (`true` or `false`).

- `==` Checks if two values are equal.
- `!=` Checks if two values are not equal.
- `>` Checks if the left value is greater than the right value.
- `>=` Checks if the left value is greater than or equal to the right value.
- `<` Checks if the left value is less than the right value.
- `<=` Checks if the left value is less than or equal to the right value.

c. Logical Operators

Logical operators are used to **combine multiple conditions** or reverse a condition’s result.

- `and`  
  Returns `true` only if **both** conditions are true.
- `or`  
  Returns `true` if **at least one** condition is true.
- `not`  
  Reverses the result of a condition.

d. Other Operators

  • in : The in operator checks whether a value exists inside a sequence, such as a list, tuple, or dictionary.
    When used with dictionaries, it evaluates against the keys.

    Jinja {{ 'status' in order }} {# Checks if 'status' key exists in the order dictionary #}

  • is : The is operator is used to apply tests to a variable.
    These tests help validate data by checking conditions such as:

    • Whether a variable is defined
    • Whether it matches a specific data type
    • Whether it satisfies a particular rule

    Jinja {{ order.total is number }} {# Checks if the total is a number #}

  • ~ :
    The ~ operator (read as tilde) is used to concatenate values into a single string.

    Before concatenation: - All operands are automatically converted to strings. - This allows numbers, variables, and text to be safely joined together.

Jinja {{ 'Total: ' ~ order.total }} {# Produces "Total: 500" if order.total is 500 #}

Jinja Data Structures

In SnapApp, Jinja offers several core data structures that allow for flexible and dynamic template creation. Lists, tuples, and dictionaries are key elements, enabling the manipulation and organization of data for rendering dynamic content efficiently.

  • Lists : A list is a mutable, ordered collection of items that can hold diverse data types without requiring prior declaration. Lists in Jinja are ideal for managing sequences of objects or values, allowing for flexible additions, removals, and retrieval of data.

##### How to initialize and access items in a list?

To define a list in Jinja, use the following syntax:

jinja {% set sampleList = [10, 20, 30, 40, 50] %}

This creates a list containing the numbers 10 through 50. An empty list can be initialized as:

Jinja {% set emptyList = [] %}

Lists are indexed, meaning each item in the list can be accessed by its position. The first element in any list is always at index 0, the second at index 1, and so on. For instance, in the list below:

Jinja {% set alphaList = ['X', 'Y', 'Z', 'A'] %}

  • X is at index 0,
  • A is at index 3.

To retrieve an item, use the index:

Jinja {{ alphaList[2] }} {# Returns 'Z' #}

##### Adding and removing items from a list

Lists in Jinja allow for dynamic changes, such as appending or removing elements.

Appending Elements: To add an item to an existing list, use the append method.

Jinja {% set myList = [2, 4, 6] %} {% append 8 to myList %}

This adds 8 to the end of myList, resulting in [2, 4, 6, 8].

Removing Elements: Use the pop() method to remove elements from a list. The method can be used with or without an index.

Jinja {% set temp = myList.pop(1) %} {# Removes the second item in the list #}

Example:

Jinja {% set myList = [100, 200, 300, 400] %} {% set temp = myList.pop(2) %} {# Removes 300 from the list #} {{ myList }} {# Outputs [100, 200, 400] #}

To find an element’s index, use the index() function:

Jinja {% set myIndex = myList.index(400) %}

  • Tuples : Tuples, similar to lists, are ordered collections but are immutable—once created, they cannot be changed. Tuples are often used for fixed data that should remain constant throughout template rendering.

##### How to create and access Tuples

To define a tuple:

Jinja {% set sampleTuple = ('SnapApp', 'is', 'Cool') %}

Elements within a tuple can be accessed via their index, just like lists:

Jinja {{ sampleTuple[1] }} {# Returns 'is' #}

A tuple with a single element requires a trailing comma:

Jinja {% set singleItemTuple = ('SnapApp',) %}

Example:

Jinja {% set pageData = [] %} {% append ('home.html', 'Home Page') to pageData %} {% append ('about.html', 'About SnapApp') to pageData %} {{ pageData[1][1] }} {# Outputs 'About SnapApp' #}

  • Dictionaries : Dictionaries are collections of key-value pairs, where each key is unique and mapped to a corresponding value. In SnapApp, dictionaries are particularly useful for storing structured data such as customer information, settings, or configuration parameters.

##### How to define and access Dictionaries?

To create a dictionary:

jinja {% set customerInfo = {"Name": "Alex", "Role": "Administrator"} %}

Access values by referencing their keys:

Jinja {{ customerInfo["Name"] }} {# Outputs 'Alex' #}

Keys in dictionaries can be strings, numbers, or None. Values can be any data type, including other dictionaries, lists, or tuples.

Jinja {% set productDetails = {"Product": "Laptop", "Price": 1500, "Stock": 30} %} {{ productDetails["Product"] }} {# Returns 'Laptop' #}


Jinja Filters

In SnapApp, filters are a powerful tool that can be used to modify data output dynamically within templates. Filters are applied to variables, allowing content transformations without modifying the original data source.

  • Filter Syntax: Filters are applied by using the pipe (|) symbol, followed by the name of the filter. The filter modifies the variable it is applied to. For instance, to convert a customer’s name to uppercase:
{{ customer["name"] | upper }}

This will convert the customer’s name to uppercase. The pipe symbol is an intuitive way to express “apply this filter to the variable.”

Filters that require additional parameters take arguments within parentheses, as demonstrated below:

{{ "product" | replace('p', 'b') }}

This replaces all occurrences of ‘p’ with ‘b’, resulting in the output broduct.

  • Chaining Filters : Multiple filters can be applied to a variable in sequence. Filters are evaluated left to right, provided that the output of one filter is a valid input for the next.

Example

{{ "apple" | upper | replace('P', 'b') }}
Output: AbbLE
{{ "apple" | replace('p', 'b') | upper }}
Output: ABBLE

Global Variables in Jinja

Jinja provides a set of global variables that are automatically available within SnapApp campaigns and projects.
These variables allow you to personalize content dynamically by accessing IDs, names, and related metadata for different entities.

  • Project Level

Project-level variables provide information about the current project.

  • {{ project['id'] }}
    Returns the unique identifier of the project.
  • {{ project['name'] }}
    Returns the name of the project.

  • Scenario / Email Campaign

These variables reference the scenario or email campaign in which the template is being used.

  • {{ scenario['id'] }}
    Fetches the unique ID of the scenario or email campaign.
  • {{ scenario['name'] }}
    Retrieves the name of the scenario or email campaign.

  • Scenario Node

Scenario node variables represent the individual action nodes inside a scenario.

  • {{ action['id'] }}
    Gets the unique ID of the action node.
  • {{ action['name'] }}
    Returns the name of the action node or email campaign.

  • Banner

Banner variables provide details about banners and their variants.

  • {{ banner['id'] }}
    Returns the banner ID.
  • {{ banner['name'] }}
    Provides the banner name.
  • {{ banner['variant']['id'] }}
    Retrieves the ID of the banner variant.
  • {{ banner['variant']['name'] }}
    Returns the name of the banner variant.

  • Experiments

Experiment variables allow access to experiment and variant details.

  • {{ experiment['id'] }}
    Fetches the ID of the experiment.
  • {{ experiment['name'] }}
    Returns the name of the experiment.
  • {{ experiment['variant']['id'] }}
    Retrieves the variant ID of the experiment.
  • {{ experiment['variant']['name'] }}
    Provides the name of the experiment variant.

  • Tag Manager

Tag-related variables are used to reference tags applied within SnapApp.

  • {{ tag['id'] }}
    Returns the tag ID.
  • {{ tag['name'] }}
    Retrieves the name of the tag.

Technical Expressions

The following expressions are more advanced or technical and are typically used in specific scenarios such as formatting timestamps, generating URLs, or creating secure random values.

These expressions are especially useful when working with APIs, public resources, or security-related data in SnapApp templates.

  • sent_timestamp | from_timestamp
    Converts a Unix timestamp into a human-readable date and time format.

  • api_base_url
    Returns the base URL for an API endpoint, useful when constructing API requests dynamically.

  • public_base_url
    Returns the base URL for public-facing app resources, such as externally accessible pages or assets.

  • random_bytes(length) | hexencode
    Generates a random byte string of the specified length and encodes it in hexadecimal format.
    Commonly used for generating secure tokens or identifiers.

  • random_bytes(length) | b64encode
    Generates a random byte string of the specified length and encodes it in Base64 format.


Functions on Data Types

Jinja exposes many built-in functions that can be applied to different data types.
These functions allow you to inspect, transform, and manipulate values directly inside templates.

The table below lists commonly available functions, grouped by data type.

Data Type Function Description
Integer int.bit_length() Returns the number of bits required to represent the integer in binary, excluding the sign and leading zeros.
Integer int.conjugate() Returns the conjugate of the number. Since complex numbers are not supported, this returns the integer itself.
Float float.as_integer_ratio() Returns a tuple of two integers whose ratio exactly represents the float value.
Float float.is_integer() Returns True if the float represents an integer value, otherwise False.
Float float.hex() Returns a hexadecimal string representation of the floating-point number.
Float float.fromhex(string) Converts a hexadecimal string into a floating-point number.
Float float.conjugate() Returns the conjugate of the float. Since complex numbers are not supported, the value remains unchanged.
String str.capitalize() Returns a copy of the string with the first character capitalized and the rest in lowercase.
String str.center(width[, fillchar]) Centers the string within the specified width, padding with the given character if needed.
String str.count(sub[, start[, end]]) Counts the number of non-overlapping occurrences of a substring.
String str.encode(encoding="utf-8", errors="strict") Encodes the string into bytes using the specified encoding.
String str.endswith(suffix[, start[, end]]) Returns True if the string ends with the specified suffix.
String str.expandtabs(tabsize=8) Replaces tab characters with spaces based on the given tab size.
String str.find(sub[, start[, end]]) Returns the lowest index of the substring, or -1 if not found.
String str.index(sub[, start[, end]]) Similar to find, but raises an error if the substring is not found.
String str.isalnum() Returns True if all characters are alphanumeric.
String str.isalpha() Returns True if all characters are alphabetic.
String str.isdigit() Returns True if all characters are digits.
String str.islower() Returns True if all cased characters are lowercase.
String str.isspace() Returns True if the string contains only whitespace characters.
String str.istitle() Returns True if the string follows title-case formatting.
String str.isupper() Returns True if all cased characters are uppercase.
String str.join(iterable) Joins elements of an iterable into a single string using the string as a separator.
String str.ljust(width[, fillchar]) Left-justifies the string, padding it to the specified width.
String str.lower() Converts all cased characters to lowercase.
String str.lstrip([chars]) Removes leading characters from the string.
String str.partition(sep) Splits the string at the first occurrence of the separator and returns a tuple.
String str.replace(old, new[, count]) Replaces occurrences of a substring with another substring.
String str.rfind(sub[, start[, end]]) Returns the highest index of the substring, or -1 if not found.
String str.rindex(sub[, start[, end]]) Like rfind, but raises an error if the substring is not found.
String str.rjust(width[, fillchar]) Right-justifies the string within the specified width.
String str.rpartition(sep) Splits the string at the last occurrence of the separator.
String str.rstrip([chars]) Removes trailing characters from the string.
String str.split(sep=None, maxsplit=-1) Splits the string into a list using the specified separator.
String str.splitlines() Splits the string at line boundaries into a list.
String str.startswith(prefix[, start[, end]]) Returns True if the string starts with the specified prefix.
String str.strip([chars]) Removes both leading and trailing characters from the string.
String str.swapcase() Converts uppercase characters to lowercase and vice versa.
String str.title() Converts the string to title case.
String str.translate(table) Maps characters in the string using a translation table.
String str.upper() Converts all cased characters to uppercase.
String str.zfill(width) Pads the string on the left with zeros until it reaches the specified width.
List list.count(item) Returns the number of occurrences of an item in the list.
List list.index(item[, start[, end]]) Returns the index of the first matching item in the list.
List list.pop([index]) Removes and returns the item at the given index.
Tuple tuple.count(item) Returns the number of occurrences of an item in the tuple.
Tuple tuple.index(item[, start[, end]]) Returns the index of the first matching item in the tuple.
Dictionary dict.fromkeys(seq[, value]) Creates a new dictionary with keys from a sequence and a shared value.
Dictionary dict.get(key[, default]) Returns the value for a key, or a default if the key is missing.
Dictionary dict.items() Returns a list of (key, value) pairs from the dictionary.

Jinja Blocks

Jinja code blocks use the syntax {% blocktype %} ... {% endblocktype %}.
They help organize template logic by grouping conditions, loops, and variable assignments into clear, readable sections.

While Jinja supports multiple block types, template importing is not currently implemented in SnapApp, which makes inheritance-based blocks less useful.

Block Types
  • Set / If Blocks

The if block enables conditional rendering based on boolean expressions.
It supports elif (else-if) and else clauses for additional conditions and fallback behavior.
Every if block must end with {% endif %}.

jinja {# If block number one #} {% if 1 < 1 %} One is less than one. {% elif 1 < 2 %} One is less than two {% else %} one is greater or equal to two {% endif %}

Output: one is less than two

Because the first condition fails, the second one gets evaluated. It evaluates to true, hence the statements following it gets rendered.

OR

Using an inline conditional expression is useful for compact logic.

jinja {% set x = 'Project' if 13 is even else 'Task' %} {{ x }}

Output:

Task

  • For Blocks

The for block is used to iterate over items in an iterable (such as lists). It starts with {% for item in iterable %} and ends with {% endfor %}. An optional {% else %} block is executed if the iterable is empty.

jinja {% for x in [] %} {{ 'Field' }} {%- else %} {{ 'Object' }} {% endfor %}

Output:

Object

jinja {% for x in [1, 2] %} {{ x }} {% else %} {{ 'Object' }} {% endfor %}

Output:

1 2

  • Recursive For Loop

Jinja supports recursive loops using the recursive flag. This is useful when working with nested lists or hierarchical data structures. The loop() function allows the loop to call itself for nested elements.

jinja {% set x = [11, [21, 22], [[23]]] %} {% for item in x recursive %} {% if item is iterable %} {{ loop(item) }} {% else %} {{ item ~ ' says hello from depth: ' ~ loop.depth }} {% endif %} {% endfor %}

Output:

11 says hello from depth: 1 21 says hello from depth: 2 22 says hello from depth: 2 23 says hello from depth: 3

  • For Loop Filtering

Filters can be applied directly in a for loop. When using filters with recursion, the recursive keyword must be placed at the end of the loop statement.

jinja {% for item in ['hello', [42], 13, 'world'] if (item is not string) recursive %} {{ loop(item) if item is iterable else item ~ ', depth: ' ~ loop.depth }} {% endfor %}

Output:

42, depth: 2 13, depth: 1

  • For Loop Variables

Inside a for loop, a special variable named loop is available. It provides useful metadata about the current iteration.

Property Type Description
loop.first Boolean True if this is the first iteration
loop.last Boolean True if this is the last iteration
loop.length Integer Total number of iterations
loop.depth Integer Current depth in a recursive loop (starts at 1)
loop.depth0 Integer Current depth in a recursive loop (starts at 0)
loop.index Integer Current iteration index starting from 1
loop.index0 Integer Current iteration index starting from 0
loop.revindex Integer Reverse index starting from 1
loop.revindex0 Integer Reverse index starting from 0
loop.cycle(arg1, arg2, …) Function Cycles through provided arguments based on the current iteration
  • loop.cycle() Helper Function

The loop.cycle() function returns items from a sequence based on the iteration count.

jinja {% for item in range(3) %} {{ loop.cycle('hello', 'SnapApp') }} {{ loop.cycle('hello', 'SnapApp') }} {% endfor %}

Output:

hello hello SnapApp SnapApp hello hello


Macros

Macros in Jinja serve as callable code blocks, analogous to functions. They enhance code clarity and efficiency by encapsulating reusable logic.

Macros are defined using the syntax {% macro macroName(args) %} … {% endmacro %}. Parameters, referred to as arguments, are passed to the macro during invocation. This enables customization and flexibility in macro execution.

{% macro printMood(day, mood='happy') %}
{{ (day | title) ~ ', Weather feels ' ~ mood ~ '!' }}
{% endmacro %}
...
{% set currMood = 'amazing' %}
{% set currDay = 'today' %}
{{ printMood(currDay, currMood) }}
Output:
Today, Weather feels amazing!

Call

The call block, closely related to macros, provides a mechanism for passing content directly to a macro. When a call block is used within a macro invocation, its content is wrapped as a macro and assigned to the ‘caller’ argument. This allows for dynamic content injection and customization within macros.

To access the passed content within the macro, the caller() function is used. This enables the macro to incorporate the provided content into its execution logic, creating more flexible and adaptable templates.

{# This one throws error because argument caller was not declared #}
{% macro errorMacro(arg1) %}
{{ errorMacro.name ~ "'s arg1 is " ~ arg1 }}
{% endmacro %}
...
{% call errorMacro() %}
This is the content of the call block.
{% callend %}

{# This one works because argument caller was declared #}
{% macro workingMacro(arg1='', caller='') %}
{{ workingMacro.name ~ "'s args are " ~ arg1 ~ ' and "' ~ caller() ~ '"' }}
{% endmacro %}
...
{% call workingMacro() %}
This is the content of the call block.
{% endcall %}
Output:
workingMacro's args are and "
This is the content of the call block."

Raw

The raw block treats its content as literal strings, preventing Jinja syntax evaluation. This is particularly useful when you need to display actual Jinja syntax within the template output.

{% raw  %}
{% set x = 5 %}
{% endraw %}
Output:
"{% set x = 5 %}"

Jinja in SnapApp

In SnapApp, Jinja templating can be used directly within the Page Builder to render dynamic data. This allows you to combine static content with dynamic values fetched from objects, records, or functions at runtime.

To access the Page Builder and learn more about its usage, refer to the documentation
SnapApp Pages

Using Jinja in Page Builder

Below is an example of Jinja code written inside the Page Builder to dynamically display values such as name and address from an object.

jinja code

In this example: - Jinja expressions are used to reference object fields - Dynamic values are injected into the page at render time - Static text and dynamic data coexist seamlessly

Rendered Output in Page

The following image shows how the page appears after rendering. The name and address fields are populated dynamically from the object for a specific record ID, while other content remains static.

page

This demonstrates how Jinja enables: - Dynamic field rendering - Record-specific data display - Clean separation between template logic and data

Pre-conditions
  • The record must exist in the object
  • The Page must receive a valid record ID
  • Field names used in Jinja must match the object schema

If these conditions are not met, the dynamic values will not render correctly.


SnapApp Functions in Jinja

In addition to basic Jinja expressions, SnapApp Functions can also be invoked within Jinja templates in the Page Builder. These functions allow you to execute queries, fetch datasets, and perform custom logic.

To learn how to create SnapApp Functions, see the documentation
SnapApp Functions and Custom Expressions

Example: Using a SnapApp Function

The following example demonstrates a SnapApp Function that retrieves all records from the CONTACTS table.

SnapApp function

Once defined, this function can be called inside a Jinja template and its results can be iterated over using a for loop.

Rendering Function Results in Page Builder

The image below shows how the data returned by the SnapApp Function is rendered on the page by iterating over the result set using Jinja.

jinja code

This approach enables: - Dynamic list rendering - Database-driven page content - Clean integration of backend logic with frontend templates

Summary
  • Jinja can be used in SnapApp Page Builder for dynamic rendering
  • Object fields can be accessed directly in templates
  • SnapApp Functions can be invoked inside Jinja
  • Returned datasets can be iterated using for loops
  • This combination allows highly dynamic, data-driven pages in SnapApp

Thank you for following these steps to configure your SnapApp components effectively If you have any questions or need further assistance, please don’t hesitate to reach out to our support team. We’re here to help you make the most out of your SnapApp experience.

For support, email us at snapapp@bluevector.ai


Generate Text