Basic Concepts
View SourceUnderstand the core ideas behind Mau.
What is Mau?
Mau is a template engine for Elixir. It takes a template string with special syntax and renders it to final output based on a context (a map of variables).
Simple equation:
Template + Context = OutputExample:
Template: "Hello {{ name }}!"
Context: %{"name" => "World"}
Output: "Hello World!"The Three Types of Content
Mau templates contain three types of content:
1. Plain Text
Regular text that appears as-is in the output:
Welcome to Mau!This renders exactly as written.
2. Expressions (with {{ }})
Expressions evaluate code and insert the result:
{{ user.name }}
{{ items | length }}
{{ 5 + 3 }}Expressions:
- Access variables with
{{ variable_name }} - Access properties with
{{ object.property }} Use filters with
{{ value | filter }}- Use expressions with
{{ 5 + 3 }}
3. Tags (with {% %})
Tags control logic but don't produce output themselves:
{% if condition %}
This appears if true
{% endif %}
{% for item in items %}
Process each item
{% endfor %}Tags:
{% if %}- Conditionals{% for %}- Loops{% assign %}- Variables- Comments:
{# This is a comment #}
Context: Your Data
The context is a map that provides data to your template:
context = %{
"user" => %{
"name" => "Alice",
"email" => "alice@example.com",
"age" => 30
},
"settings" => %{
"theme" => "dark",
"notifications" => true
}
}Access context data in templates:
{{ user.name }}→ "Alice"{{ user.age }}→ 30{{ settings.theme }}→ "dark"
Variables
Variables store data that you reference in templates.
Simple Variables
context = %{"greeting" => "Hello"}
template = "{{ greeting }} World"
# Output: "Hello World"Nested Variables
context = %{
"user" => %{
"profile" => %{
"name" => "Bob"
}
}
}
template = "{{ user.profile.name }}"
# Output: "Bob"Array/List Access
context = %{"items" => ["apple", "banana", "cherry"]}
template = "First: {{ items[0] }}, Last: {{ items[2] }}"
# Output: "First: apple, Last: cherry"Variable Assignment
Create variables in your template:
template = """
{% assign greeting = "Hello" %}
{{ greeting }} World
"""
# Output: "Hello World"Filters: Transform Data
Filters modify values. Use the pipe | to apply them:
{{ value | filter_name }}Common filters:
{{ "hello" | capitalize }} # "Hello"
{{ "HELLO" | lower_case }} # "hello"
{{ [1, 2, 3] | length }} # 3
{{ [3, 1, 2] | sort }} # [1, 2, 3]
{{ "a,b,c" | split(",") }} # ["a", "b", "c"]
{{ items | first }} # First item
{{ items | last }} # Last itemChain multiple filters:
{{ text | strip | capitalize | upper_case }}This applies filters left to right.
Expressions: Calculate Values
Perform calculations and comparisons in {{ }}:
Arithmetic
{{ 5 + 3 }} # 8
{{ 10 - 4 }} # 6
{{ 3 * 4 }} # 12
{{ 10 / 2 }} # 5.0Comparisons
{{ 5 > 3 }} # true
{{ 5 < 3 }} # false
{{ 5 == 5 }} # true
{{ 5 != 3 }} # true
{{ 5 >= 5 }} # true
{{ 5 <= 10 }} # trueLogical Operations
{{ true and false }} # false
{{ true or false }} # true
{{ not false }} # trueString Concatenation
{{ "Hello " + "World" }} # "Hello World"
{{ first_name + " " + last_name }}Control Flow: If/Else
Basic If
{% if user.active %}
User is active
{% endif %}If/Else
{% if user.admin %}
Admin Panel
{% else %}
User Area
{% endif %}If/Elsif/Else
{% if user.role == "admin" %}
Admin
{% elsif user.role == "moderator" %}
Moderator
{% else %}
User
{% endif %}Loops: Iterate Collections
For Loop
{% for item in items %}
{{ item }}
{% endfor %}Loop Variables
Access loop information with forloop:
{% for item in items %}
{{ forloop.index }}: {{ item }}
{% endfor %}Loop variables:
forloop.index- Current position (1-based)forloop.index0- Current position (0-based)forloop.first- true on first iterationforloop.last- true on last iterationforloop.length- Total count
Nested Loops
{% for category in categories %}
{{ category.name }}:
{% for item in category.items %}
- {{ item }}
{% endfor %}
{% endfor %}Truthy and Falsy
In conditionals, values are evaluated as truthy or falsy:
Falsy values:
falsenil""(empty string)[](empty list){}(empty map)
Truthy values:
- Everything else, including:
true- Non-empty strings:
"hello" - Non-zero numbers:
5,-1 - Non-empty lists:
[1, 2, 3] - Non-empty maps:
%{"key" => "value"}
Example:
{% if user.email %}
Email: {{ user.email }}
{% endif %}This renders only if user.email is not falsy.
Comments
Comments are not rendered:
{# This is a comment and won't appear in output #}
Template text
{# Comments can be multi-line
and span across lines #}Whitespace Control
Control whitespace around expressions and tags:
{{ name }} {# Spaces around are preserved #}
{{- name }} {# Trim left whitespace #}
{{ name -}} {# Trim right whitespace #}
{{- name -}} {# Trim both #}Example:
template = """
Items:
{% for item in items %}
- {{ item }}
{% endfor %}
"""
# vs with whitespace control:
template = """
Items:
{%- for item in items %}
- {{ item }}
{%- endfor %}
"""Type Preservation
By default, all template output is a string:
Mau.render("{{ 42 }}", %{})
# {:ok, "42"} <- string, not numberEnable type preservation to maintain types:
Mau.render("{{ 42 }}", %{}, preserve_types: true)
# {:ok, 42} <- number, not string
Mau.render("{{ active }}", %{"active" => true}, preserve_types: true)
# {:ok, true} <- boolean preserved
Mau.render("{{ items }}", %{"items" => [1, 2, 3]}, preserve_types: true)
# {:ok, [1, 2, 3]} <- list preservedImportant: Mixed content (text + expressions) always returns strings:
Mau.render("Count: {{ 42 }}", %{}, preserve_types: true)
# {:ok, "Count: 42"} <- string because it contains textSummary
| Concept | Syntax | Purpose |
|---|---|---|
| Text | Plain text | Static content |
| Expression | {{ value }} | Insert dynamic data |
| Filter | | filter_name | Transform data |
| If | {% if condition %} | Conditional rendering |
| For | {% for item in list %} | Iterate collections |
| Assign | {% assign var = value %} | Create variables |
| Comment | {# comment #} | Non-rendered text |
Next Steps
- Your First Template - Build a complete example
- Template Language Reference - Detailed feature guides
- Email Template Examples - Real-world use cases