Basic Concepts

View Source

Understand 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 = Output

Example:

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 item

Chain 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.0

Comparisons

{{ 5 > 3 }}              # true
{{ 5 < 3 }}              # false
{{ 5 == 5 }}             # true
{{ 5 != 3 }}             # true
{{ 5 >= 5 }}             # true
{{ 5 <= 10 }}            # true

Logical Operations

{{ true and false }}     # false
{{ true or false }}      # true
{{ not false }}          # true

String 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 iteration
  • forloop.last - true on last iteration
  • forloop.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:

  • false
  • nil
  • "" (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 number

Enable 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 preserved

Important: Mixed content (text + expressions) always returns strings:

Mau.render("Count: {{ 42 }}", %{}, preserve_types: true)
# {:ok, "Count: 42"}  <- string because it contains text

Summary

ConceptSyntaxPurpose
TextPlain textStatic content
Expression{{ value }}Insert dynamic data
Filter| filter_nameTransform 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