Scriban Templating¶
Deepstaging includes Scriban as a native templating engine for rendering structured text content — email HTML, SMS bodies, notification copy, webhook payloads, and any other non-code output that has a fixed shape with variable data. Templates are compile-time validated against typed models, so a typo like {{ order.Totl }} is a build error, not a runtime 500.
Why Templating?¶
Most applications have content that follows a pattern: welcome emails, order confirmations, SMS verification codes, push notification bodies, audit log messages. Without a templating system, this content lives as interpolated strings scattered across handler code — hard to find, hard to review, impossible to validate at compile time.
Deepstaging's Scriban integration makes template-driven content a first-class concern:
- Compile-time validation — template variables are checked against your model at build time
- Separation of content from logic — templates live in dedicated files, not buried in C# handlers
- Consistent syntax — one templating language across email, SMS, notifications, and beyond
- Zero runtime reflection — templates are pre-compiled during the build
Quick Start¶
Define a template file and a typed model, then render:
// Models/WelcomeEmailModel.cs
public sealed record WelcomeEmailModel(string UserName, string ActivationUrl);
<!-- Templates/Email/Welcome.scriban-html -->
<h1>Welcome, {{ user_name }}!</h1>
<p>Click <a href="{{ activation_url }}">here</a> to activate your account.</p>
// In your command handler
var html = Template.Render("Email/Welcome", new WelcomeEmailModel(
UserName: buyer.Name,
ActivationUrl: $"https://app.example.com/activate/{token}"));
var result = await emailService.SendAsync(new EmailMessage
{
From = new EmailAddress("noreply@example.com"),
To = [new EmailAddress(buyer.Email)],
Subject = "Welcome!",
HtmlBody = html
});
Supported Modules¶
Each module that benefits from templating has a dedicated page explaining usage patterns. The table below links to the relevant section in each module's docs.
| Module | Template Use | Example |
|---|---|---|
| HTML and plain-text email bodies | Order confirmations, welcome emails, password resets | |
| SMS | Message body text | Verification codes, shipping updates, appointment reminders |
| Notifications | Push notification title and body | {{ order_id }} has shipped, in-app alert copy |
| Webhooks | Outbound webhook JSON payloads | Custom event payload formats for third-party integrations |
| Audit | Human-readable audit trail messages | {{ actor }} updated {{ entity_type }} {{ entity_id }}: {{ changes }} |
| Jobs | Failure and retry notification messages | Dead-letter alerts, job summary reports |
| Integration Events | Dead-letter and poison-message alert content | Structured alert bodies for failed event processing |
Template Syntax¶
Scriban uses {{ }} delimiters with a clean, Ruby-like syntax. Full reference: Scriban Language.
Variables¶
Hello {{ user_name }},
Your order {{ order_id }} has been confirmed.
Total: ${{ order_total | math.format "0.00" }}
Conditionals¶
{{ if has_tracking_url }}
Track your package: {{ tracking_url }}
{{ else }}
Tracking information will be available soon.
{{ end }}
Loops¶
Your order contains {{ items | array.size }} items:
{{ for item in items }}
- {{ item.name }} × {{ item.quantity }} — ${{ item.price | math.format "0.00" }}
{{ end }}
Filters (Pipes)¶
Scriban filters transform values inline:
{{ name | string.upcase }}
{{ created_at | date.to_string "%B %d, %Y" }}
{{ description | string.truncate 100 }}
{{ amount | math.format "0.00" }}
Whitespace Control¶
Use - to strip whitespace around tags — useful for clean text output in SMS and notifications:
Best Practices¶
Keep templates focused on content, not logic¶
Templates should render content, not make business decisions. Keep conditionals simple — if a template needs complex branching, the model should pre-compute the values.
{{- # Good: model pre-computes the greeting -}}
{{ greeting }},
{{- # Bad: business logic in the template -}}
{{ if is_premium_user && renewal_date < today }}Dear valued member{{ else }}Hi{{ end }},
Use plain-text fallbacks for email¶
Always provide both HTML and plain-text templates for email. SMS and push notifications are inherently plain text.
Templates/
├── Email/
│ ├── Welcome.scriban-html ← HTML version
│ └── Welcome.scriban-txt ← plain-text fallback
├── Sms/
│ └── VerificationCode.scriban-txt
└── Notifications/
└── OrderShipped.scriban-txt
Name templates by intent, not by module¶
Use names that describe the content, not the delivery channel — the same template might be used across email and in-app notifications: