A text template engine for .NET. Heddle compiles templates written in its small, purpose‑built language into reusable, strongly‑typed renderers. Templates can embed real C# expressions, define and inherit reusable named blocks, compose output through extension chains, and control HTML encoding per directive.
@model(){{dynamic}}
<p>Hi @(Name) — you have @int(Count) new comments.</p>
HeddleTemplate.Configure(typeof(Program).Assembly);
var source = "@model(){{dynamic}}\n<p>Hi @(Name) — you have @int(Count) new comments.</p>";
using var template = new HeddleTemplate(source, new CompileContext(new TemplateOptions { AllowCSharp = true }));
string html = template.Generate(new { Name = "Ada", Count = 3 });
// <p>Hi Ada — you have 3 new comments.</p>| Package | Purpose |
|---|---|
Heddle |
Core engine: parser host, compiler, runtime, built‑in extensions. |
Heddle.Language |
ANTLR grammar + generated lexer/parser and editor assets. |
Full documentation lives in docs/:
- Getting Started — build and render your first template.
- Language Reference — every Heddle construct and its nuances.
- Built‑in Extensions —
list,if,date,money, and more. - C# API Reference —
HeddleTemplate, options, contexts, results. - Writing Custom Extensions — add your own directives.
- Architecture — the lex → parse → compile → render pipeline.
- Building & Testing — SDK, scripts, tests, packaging, CI.
Heddle sits in a small niche: a compiled, statically‑typed template language whose entire control‑flow vocabulary is library, not grammar. Here is where it lands against the engines people usually weigh it against.
| Heddle | Razor | Liquid / Scriban | Handlebars / Mustache | Go text/template |
|
|---|---|---|---|---|---|
| Execution | Compiled to an exec‑ready document | Compiled | Interpreted | Interpreted | Interpreted |
| Typing | Static, per use site | Static | Dynamic | Dynamic | Dynamic |
| Logic in templates | Full C# (opt‑in) | Full C# | Sandboxed filters | Logic‑less + helpers | Limited + funcs |
| Control flow | Extensions (a library) | Keywords | Tags | Block helpers | Keywords |
| Context model | Relative: current / ::root |
Absolute (Model.X) |
Mostly global | Relative stack | Relative (. / $) |
| Composition | Definition inheritance + override | Layouts / sections / partials | Partials / includes | Partials | Heddle / blocks |
| Untrusted templates | No (or run in no‑C# mode) | No | Yes (sandboxed) | Yes (logic‑less) | Partial |
| Reach / ecosystem | .NET only, small | .NET, excellent | Large | Large, polyglot | Large |
What's genuinely distinctive (the combination, more than any single trait):
- Extension‑only core. There are no
if/for/includekeywords — those are extensions, so the language grows by adding a class, not by changing the grammar. The:chain composes them right‑to‑left, with a single chained channel carrying the loop index, the piped value, and@out()content. - Abstract, late‑bound sections. A definition with no
:: Typeis compiled against the concrete model at each call site — closer to a C++ template (monomorphised, type‑checked per use) than a C# generic (compiled once behind constraints). Reuse one section across many model shapes, each statically checked. - Composition without coupling. Definition inheritance/override are declarative extension points: unlike Razor sections (which bind "backwards" and pin the rendered page as the layout's final consumer), a Heddle page can be split into independent templates recombined by a layout with no runtime cost, and any page can serve as a base for another.
- Compiled to an execution‑ready document. A template becomes an in‑memory tree of extension calls wired to compiled accessors — member paths to expression‑tree delegates, embedded C# to Roslyn delegates — so nothing is reflected or re‑parsed per render. It renders faster than Razor with fewer allocations on a like‑for‑like page.
Best fit: performance‑sensitive, first‑party .NET rendering by a team that values typed
templates and component‑style composition. Poor fit: untrusted user‑supplied templates
(the security model is all‑or‑nothing on AllowCSharp, not a sandbox), polyglot stacks, or
teams wanting a large ecosystem and batteries‑included tooling. The language also trades some
ergonomics for its small core — dense sigils and no else/elif (compose @if/@ifnot
instead). See the Language Reference for the full picture.
Heddle compiles each template into an execution‑ready document — an in‑memory tree of extension calls wired to compiled accessors (member paths to expression‑tree delegates, embedded C# to Roslyn delegates). Rendering walks that document, so it does not re‑parse, reflect, or pay per‑call activation, section, or dependency‑injection overhead at run time. The repository includes a BenchmarkDotNet suite (src/Heddle.Performance) that renders a realistic home page — a layout plus several reusable templates and a dozen component/extension invocations — and compares it head‑to‑head against the equivalent ASP.NET Core Razor page (RenderTemplateEngine vs RenderRazor).
On that like‑for‑like workload, Heddle renders the page faster than Razor and allocates less
memory ([MemoryDiagnoser] is enabled). The advantage comes from two places:
- Execution — walking the pre‑compiled document avoids the partial/section/view‑component machinery Razor invokes per component (see Architecture → Performance).
- Composition — Heddle definitions are declarative, decoupled extension points. Unlike Razor sections, which bind "backwards" and force the rendered page to be the layout's final consumer, a Heddle template can be split into independent reusable templates recombined by a layout with no runtime cost, and any page can itself serve as a base for another. See Language Reference → inheritance.
Run it yourself:
dotnet run -c Release --project src/Heddle.Performance
dotnet build -c ReleaseRequires the .NET SDK 10.0 (see global.json). See docs/building.md for details.
Contributions are welcome. Please read CONTRIBUTING.md first — it
covers the development setup, the pull-request flow, the required
DCO sign-off (git commit -s), and the policy on
AI-assisted contributions. By contributing you agree your work is licensed under Apache-2.0.
Copyright © Aliaksandr Kukrash and the Heddle contributors.
Licensed under the Apache License, Version 2.0 — see LICENSE, NOTICE, and THIRD-PARTY-NOTICES.md.