Whitespace Control
By default, all text outside a {tag} is emitted verbatim. That means cleanly formatted templates — block tags on their own indented lines — emit extra blank lines and leading whitespace that may not belong in the output. Whitespace control trims this on a per-tag basis or globally.
What the engine controls
Whitespace control applies only to non-simple tags — the block-shaped or annotation-shaped tags that template authors typically indent on their own lines:
{#if}/{/if}— conditional block{#each}/{/each}— iteration block{#with}/{/with}— scope-shift block{>}— partials{!}— comments{?var:}and{?:}— variables{@}— debug tags
Simple bind tags ({Name}, {:myVar}) do not accept trim markers. Their position is part of the output by design.
Per-tag trim markers
A single - immediately after the open brace or immediately before the close brace activates trimming:
| Marker placement | Effect |
|---|---|
{-#if …} | Trim whitespace before the tag, not including newlines |
{#if …-} | Trim whitespace after the tag, including the first newline |
{-#if …-} | Trim on both sides |
The asymmetry is deliberate: left-trim removes indentation in front of the tag while preserving line structure; right-trim absorbs the newline the author put after the tag for readability, so the next line of output starts where you want it.
Example
Data:
var person = new
{
Certifications = new[] { "mcse", "mcitp", "mcts" },
Name = new { First = "John", Last = "Doe" }
};Without trim markers:
<p>Hello {Name.First}</p>
<div>
{#if Certifications}
<p>We see you have the following certs:</p>
<ul>
{#each Certifications}
<li>{$}</li>
{/each}
</ul>
{/if}
{#if !Certifications}
We see you don't have any certs.
{/if}
</div>Default output:
<p>Hello John</p>
<div>
<ul>
<li>mcse</li>
<li>mcitp</li>
<li>mcts</li>
</ul>
</div>With trim markers:
<p>Hello {Name.First}</p>
<div>
{-#if Certifications-}
<p>We see you have the following certs:</p>
<ul>
{-#each Certifications-}
<li>{$}</li>
{-/each-}
</ul>
{-/if-}
{-#if !Certifications-}
We see you don't have any certs.
{-/if-}
</div>Trimmed output:
<p>Hello John</p>
<div>
<ul>
<li>mcse</li>
<li>mcitp</li>
<li>mcts</li>
</ul>
</div>Global trim with TrimWhitespace = true
Marking every applicable tag with - markers is tedious for large templates. Setting TemplateEngine.TrimWhitespace = true applies the trim behavior to every eligible tag without needing markers:
ngin.TrimWhitespace = true;Per-tag retain marker with +
When TrimWhitespace = true is in effect, the + retain marker opts a specific tag out of trimming. Placement mirrors the - marker:
| Marker placement | Effect |
|---|---|
{+#each …} | Retain whitespace before the tag |
{#each …+} | Retain whitespace after the tag |
{+#each …+} | Retain on both sides |
Example:
ngin.TrimWhitespace = true;Template:
<ul>
{+#each Items+}
<li>{$}</li>
{+/each+}
</ul>Result:
<ul>
<li>A</li>
<li>B</li>
</ul>Without the + markers under TrimWhitespace = true, the same template would collapse to:
<ul> <li>A</li> <li>B</li></ul>Choosing a strategy
For most output formats:
- Free-form text or templates emitting whitespace-significant output — leave
TrimWhitespace = falseand add-markers only where needed. - HTML, JSON, SQL, code generation — set
TrimWhitespace = trueand add+markers in the rare spots that need preserved blank lines.
The two markers are independent: - and + are never combined on the same tag.
Next steps
- Conditionals, Iteration, With Blocks — the block tags that most commonly need trimming
- Comments — also affected by trim markers