Overview
The IRenderer
interface defines a contract for rendering a template with a model, while reusing the expensive setup parts of the rendering process, such as template parsing and context binding.
It enables you to prepare a template once, bind the rendering context, and render it multiple times efficiently with different data models.
public interface IRenderer
{
string Render(object model);
}
Each ITemplateEngine
implementation builds an IRenderer
instance that:
- Compiles or parses the template once
- Initializes engine-specific context only once (if applicable)
- Reuses the compiled representation for multiple
Render(...)
calls
This reduces runtime overhead and improves performance in scenarios like batch processing or repeated rendering of the same structure.
Why Use Cached Rendering?
Aspect | ITemplateEngine.Render(...) |
IRenderer.Render(...) |
---|---|---|
Performance | Re-compiles template and context every time | ✅ Caches parsed/compiled templates |
Context Reuse | Rebuilds context per call | ✅ Caches context creation logic (where supported) |
Efficiency | Suitable for one-off rendering | ✅ Optimized for many renders with same template |
Memory | Reallocates internals each call | ✅ Shares compiled structures where supported |
Render Pipeline: What Gets Cached?
The rendering process involves three core elements:
- Compiled Template: The parsed, compiled version of the template source string.
- Rendering Context: Template-specific state (partials, formatters, mappings).
- Model Binding: The actual input data used at render time.
Here’s a breakdown of what is cached and what is created per call for each template engine:
Engine | Compiled Template ✅ | Context Reuse 🔄 | Notes |
---|---|---|---|
Scriban | ✅ Template.Parse() |
✅ via TemplateContext factory |
Full reuse of compiled template and shared context strategy |
SmartFormat | ❌ (uses plain string) | ❌ none | No pre-compilation or context reuse |
StringTemplate | ✅ CompiledTemplate |
✅ via TemplateGroup |
Group holds shared templates and compiled representation |
Morestachio | ✅ Renderer and AST cached | ✅ via IRenderer |
Parser + compiled renderer retained |
Handlebars | ✅ Compiled delegate | ✅ if IHandlebars reused |
Parser + compiled renderer |
Fluid | ✅ IFluidTemplate |
✅ TemplateContext per model |
Efficient reuse with pre-compiled structure |
DotLiquid | ✅ Template.Parse() |
❌ Hash is recreated |
Context is built fresh each time unless you reuse Hash manually |
✅ Compiled Template: Caching avoids re-parsing the same template string
🔄 Context Reuse: Shared context (partials, helpers) setup — varies by engine
Model Binding, always occurs per render call — this is never cached.
Example Usage
If you plan to render the same template multiple times (with different models), use Prepare(...)
from an ITemplatEngine
to get an IRenderer
, then call .Render(model)
on it. This avoids redundant parsing, compiling, or binding overhead.
// Prepare once
var factory = TemplateEngineFactory.Default;
var engine = factory.Create(".scriban");
var renderer = engine.Prepare("Hello ");
// Render many times
var result1 = renderer.Render(new { Name = "Alice" });
var result2 = renderer.Render(new { Name = "Bob" });
Summary
IRenderer
is a high-performance rendering abstraction designed for template reuse- It avoids re-parsing or re-binding context on every render
- Ideal when rendering the same template repeatedly with different data
- Backed by engine-specific optimizations (e.g., compiled Scriban templates, Fluid parsing, StringTemplate groups)