Enterprise-grade, high-performance template engine for .NET 8+
JG.WebKit.Views is a blazing-fast, feature-rich template engine designed for production workloads. Built from the ground up for .NET 8+, it provides compiled delegate rendering, comprehensive syntax support, and enterprise-ready features like hot-reload, caching, and extensibility.
- High Performance - Compiled delegate rendering with zero-allocation HTML escaping
- Rich Syntax - Variables, conditionals, loops, partials, layouts, helpers
- Extensible - Custom helpers, template providers, and hooks
- Hot Reload - FileSystemWatcher integration for instant template updates
- Smart Caching - Template compilation caching with granular invalidation
- Secure - HTML escaping by default, XSS protection built-in
- Flexible - File system or in-memory providers, perfect for testing
- Well Documented - Full XML docs, comprehensive guides, real-world examples
dotnet add package JG.WebKit.Viewsusing JG.WebKit.Views;
using JG.WebKit.Views.Providers;
// Set up the engine
var provider = new FileTemplateProvider("./templates");
var options = new ViewEngineOptions();
var engine = ViewEngineFactory.Create(provider, options);
// Prepare data
var data = new Dictionary<string, object?>
{
["title"] = "Welcome",
["user"] = new { Name = "John", Email = "john@example.com" }
};
var context = new TemplateContext(data);
// Render
var html = await engine.RenderAsync("index", context);var options = new ViewEngineOptions
{
// Template settings
TemplateExtension = ".html",
LayoutPath = "_layouts",
PartialPath = "_partials",
// Security
AllowRawOutput = false, // Disable {{{ }}} for security
// Performance
CacheCompiledTemplates = true,
MaxIncludeDepth = 10,
// Asset handling
AssetBasePath = "/assets",
CdnBaseUrl = "https://cdn.example.com",
AssetVersionHash = "abc123" // For cache busting
};public class MarkdownHelper : ITemplateHelper
{
public string Name => "markdown";
public string Execute(object?[] arguments, TemplateContext context)
{
if (arguments.Length == 0) return string.Empty;
var markdown = arguments[0]?.ToString() ?? string.Empty;
return Markdig.Markdown.ToHtml(markdown);
}
}
// Register helper
var helpers = new Dictionary<string, ITemplateHelper>
{
["markdown"] = new MarkdownHelper()
};
var engine = ViewEngineFactory.Create(provider, options, helpers);
// Use in template
{{ markdown post.content }}var data = new Dictionary<string, object?> { ["page"] = pageData };
var globals = new Dictionary<string, object?>
{
["siteName"] = "My Website",
["year"] = 2026,
["config"] = appConfig
};
var context = new TemplateContext(data, globals);
// Globals available in all templates and partials
{{ siteName }} - {{ year }}// Invalidate specific template
await engine.InvalidateCacheAsync("blog/post");
// Clear entire cache
await engine.InvalidateAllAsync();var provider = new FileTemplateProvider(
"./templates",
enableHotReload: true // Auto-reload on file changes
);JG.WebKit.Views is optimized for production workloads:
- Template Compilation: 1-2ms for typical templates
- Cached Rendering: <100μs for simple templates
- Memory Efficient: Zero allocations in hot paths
- Fast Expressions: 10-50μs evaluation time
- Scales Linearly: Tested with 10,000+ item loops
Benchmark: Rendering a complex blog post page (layout + 5 partials + 50 comments):
- First render (cold): ~5ms
- Cached renders: ~200μs
- Memory: <1KB allocations per render
// Controller
public async Task<IActionResult> Post(string slug)
{
var post = await _db.Posts.FindAsync(slug);
var data = new Dictionary<string, object?>
{
["post"] = post,
["comments"] = await _db.Comments.Where(c => c.PostId == post.Id).ToListAsync(),
["relatedPosts"] = await _db.Posts.Where(p => p.Category == post.Category).Take(5).ToListAsync()
};
var context = new TemplateContext(data);
var html = await _viewEngine.RenderAsync("blog/post", context);
return Content(html, "text/html");
}Template: blog/post.html
// Email service
public async Task SendWelcomeEmail(User user)
{
var data = new Dictionary<string, object?>
{
["user"] = user,
["verificationLink"] = GenerateVerificationLink(user.Id)
};
var context = new TemplateContext(data);
var html = await _viewEngine.RenderAsync("emails/welcome", context);
await _emailService.SendAsync(user.Email, "Welcome!", html);
}Template: emails/welcome.html
// API endpoint with templated responses
[HttpGet("api/widget/{id}")]
public async Task<IActionResult> GetWidget(int id)
{
var widget = await _db.Widgets.FindAsync(id);
var data = new Dictionary<string, object?> { ["widget"] = widget };
var context = new TemplateContext(data);
var html = await _viewEngine.RenderStringAsync(
"<div class='widget'>{{ widget.name }}: ${{ widget.price }}</div>",
context
);
return Content(html, "text/html");
}- API Reference - Complete API documentation
- User Guide - In-depth usage guide
- Syntax Reference - Template syntax documentation
- Performance Guide - Optimization tips
- Migration Guide - Migrating from other engines
# Run all tests
dotnet test
# Run with coverage
dotnet test /p:CollectCoverage=trueTest Coverage: 175 tests, 100% pass rate, <1s execution time
Contributions welcome! Please read CONTRIBUTING.md for guidelines.
Apache License 2.0 - see LICENSE for details.
Built with ❤️ for the .NET community. Inspired by Handlebars, Liquid, and Razor syntax.
Version: 1.0.0 | Released: March 6, 2026 | Status: Production Ready