Skip to main content
Maizzle includes some special tags designed to help you with templating logic.

Conditionals

You can use if/elseif/else conditionals in your email templates. For example, the Starter uses it to output a preheader in its Layout:
emails/example.html
<if condition="page.preheader">
  <div class="hidden">{{ page.preheader }}</div>
</if>
Of course, you can create more complex conditions:
emails/example.html
<if condition="page.env === 'node'">
  <p>Using Maizzle programmatically</p>
</if>
<elseif condition="page.env === 'production'">
  <p>We are in production!</p>
</elseif>
<else>
  <p>We are probably developing locally.</p>
</else>

Custom conditionals tag

You may customize the conditional tag names:
config.js
export default {
  expressions: {
    conditionalTags: ['when', 'ifnotthen', 'otherwise'],
  }
}
Example:
emails/example.html
<when condition="page.env === 'node'">
  <p>Using Maizzle programmatically</p>
</when>
<ifnotthen condition="page.env === 'production'">
  <p>We are in production!</p>
</ifnotthen>
<otherwise>
  <p>We are probably developing locally.</p>
</otherwise>

Template

The <template> tag will only return its contents. You can use it to apply a filter to a string, for example:
emails/example.html
<template uppercase>test</template>
Result:
TEST
… or to compile a markdown string:
emails/example.html
<template markdown>
  # Hello, world!
</template>
Result:
<h1>Hello, world!</h1>

Preserving template tags

If you actually need to output a <template> tag in the compiled HTML, you may use the preserve attribute:
emails/example.html
<template preserve>
  test
</template>
Result:
<template>
  test
</template>

Outlook

Wrap content in MSO conditional comments to show it only in Outlook 2007-2021 on Windows:
emails/example.html
<outlook>
  <div>Show this in all Outlook versions</div>
</outlook>
That will output:
<!--[if mso]>
  <div>Show this in all Outlook versions</div>
<![endif]-->
Of course, there’s also a tag for showing content in all email clients except in Outlook:
emails/example.html
<not-outlook>
  <div>All Outlooks (on Windows) will ignore this</div>
</not-outlook>
Result:
<!--[if !mso]><!-->
  <div>All Outlooks (on Windows) will ignore this</div>
<!--<![endif]-->
The <outlook> tag supports various combinations of attributes that will help with showing or hiding content in specific Outlook versions:
  • only - show only in these Outlook versions
  • not - show in all versions except these
  • lt - all versions before this (not including it, i.e. lower than)
  • lte - all versions before this (including it, i.e. lower than or equal to)
  • gt - all versions after this (not including it, i.e. greater than)
  • gte - all versions after this (including it, i.e. greater than or equal to)
For example:
emails/example.html
<outlook only="2013">
  <div>Show only in Outlook 2013</div>
</outlook>
Result:
<!--[if mso 15]>
  <div>Show only in Outlook 2013</div>
<![endif]-->
The only and not attributes support multiple values, separated with a comma:
emails/example.html
<outlook only="2013,2016">
  <div>Show only in Outlook 2013 and 2016</div>
</outlook>
Result:
<!--[if (mso 15)|(mso 16)]>
  <div>Show only in Outlook 2013 and 2016</div>
<![endif]-->
You may also combine attributes:
emails/example.html
<outlook gt="2003" lte="2013">
  <div>Show in 2007, 2010, 2013</div>
</outlook>
Result:
<!--[if (gt mso 11)&(lte mso 15)]>
  <div>Show in 2007, 2010, 2013</div>
<![endif]-->

Custom Outlook tag

Of course, you may customize the <outlook> tag name:
config.js
export default {
  outlook: {
    tag: 'mso',
  }
}
You’d then use it like this:
emails/example.html
<mso only="2013">Show only in Outlook 2013</mso>
<not-mso>Hide from all Outlooks</not-mso>

Switch

Need to use a switch statement?
emails/example.html
<switch expression="page.user.subscription">
  <case n="'monthly'">
    <p>Your monthly subscription is about to renew.</p>
  </case>
  <case n="'yearly'">
    <p>Heads up! Yearly renewal coming soon, make sure you have enough money in your account.</p>
  </case>
  <default>
    <p>Your subscription will soon renew.</p>
  </default>
</switch>

Custom switch tag

You may define custom tags for the switch statement:
config.js
export default {
  expressions: {
    switchTags: ['handle', 'when', 'fallback'],
  }
}
Example:
emails/example.html
<handle expression="page.env">
  <when n="'production'">
    production
  </when>
  <fallback>
    fallback content
  </fallback>
</handle>

Loops

You can iterate over arrays and objects with the <each> tag. For arrays:
emails/example.html
<each loop="item, index in someArray">
  <p>{{ index }}: {{ item }}</p>
</each>
For objects:
emails/example.html
<each loop="value, key in anObject">
  <p>{{ key }}: {{ value }}</p>
</each>

Loop meta

Inside a loop you will have access to a {{ loop }} object that contains information about the loop currently being executed:
  • loop.index - the current iteration of the loop (0 indexed)
  • loop.remaining - number of iterations until the end (0 indexed)
  • loop.first - boolean indicating if it’s the first iteration
  • loop.last - boolean indicating if it’s the last iteration
  • loop.length - total number of items
Example:
emails/example.html
<each loop="item, index in [1,2,3]">
  <p>Number of iterations until the end: {{ loop.remaining }}</p>
</each>

Custom loop tag

You may customize the name of the loop tag:
config.js
export default {
  expressions: {
    loopTags: ['for'],
  }
}
You can now use a <for> tag instead:
emails/example.html
<for loop="item, index in [1,2,3]">
  <p>{{ item }}</p>
</for>

Scope

Use <scope> tags to provide a data context to the content inside. Imagine we had this data in our config.js:
config.js
export default {
  roles: {
    author: { name: 'John' },
    editor: { name: 'Jane' },
  }
}
We could provide each object as a scope, so we can then access it from the context, instead of going up to the parent:
emails/example.html
<!-- Will output 'John', no need to write {{ page.roles.author.name }} -->
<scope with="page.roles.author">
  {{ name }}
</scope>

<!-- Will output 'Jane' -->
<scope with="page.roles.editor">
  {{ name }}
</scope>

Custom scope tag

You may customize the <scope> tag name:
config.js
export default {
  expressions: {
    scopeTags: ['context'],
  }
}
Example:
emails/example.html
<!-- Will output 'Jane' -->
<context with="page.roles.editor">
  {{ name }}
</context>

Fetch

You can fetch and display remote content in your email templates:
emails/example.html
<fetch url="https://jsonplaceholder.typicode.com/users">
  <each loop="user in response">
    {{ user.name }}
  </each>
</fetch>
Inside the <fetch> tag, you have access to a {{ response }} variable.

Fetch options

You may use the fetch key to customize options:
config.js
export default {
  fetch: {
    tags: ['get'], // default ['fetch', 'remote']
    attribute: 'resource', // default 'url'
    ofetch: {}, // pass options to the `ofetch` package
    preserveTag: true, // default false
    expressions: {}, // configure expressions in fetch context
  }
}

Raw

Need to skip tag and expressions parsing in a whole block?
emails/example.html
<raw>
  This will not be parsed:
  <if condition="page.env">
    {{ page.env }}
  </if>
  Neither will this expression: {{ page.env }}
</raw>
Result:
build_production/example.html
This will not be parsed:
<if condition="page.env">
  {{ page.env }}
</if>
Neither will this expression: {{ page.env }}

Custom raw tag

The <raw> tag name may be customized:
config.js
export default {
  expressions: {
    ignoredTag: 'verbatim',
  }
}
Example:
emails/example.html
<verbatim>
  This will not be parsed: {{ page.env }}
</verbatim>

Env

You may output content based on the current Environment through the <env:> tag:
emails/example.html
<env:local>
  This will only show in local.
</env:local>

<env:production>
  This will only show in production.
</env:production>
If the tag doesn’t match the current Environment, it will be removed from the output. In this example, running maizzle build production will output:
This will only show in production.
You may also output content in all environments except a specific one:
emails/example.html
<not-env:production>
  Won't render in `production`.
</not-env:production>
With the example above, running maizzle build production will remove that block from the output, but it will be shown when you run maizzle build or maizzle serve.

Build docs developers (and LLMs) love