JG.WebKit.Views
1.0.0
dotnet add package JG.WebKit.Views --version 1.0.0
NuGet\Install-Package JG.WebKit.Views -Version 1.0.0
<PackageReference Include="JG.WebKit.Views" Version="1.0.0" />
<PackageVersion Include="JG.WebKit.Views" Version="1.0.0" />
<PackageReference Include="JG.WebKit.Views" />
paket add JG.WebKit.Views --version 1.0.0
#r "nuget: JG.WebKit.Views, 1.0.0"
#:package JG.WebKit.Views@1.0.0
#addin nuget:?package=JG.WebKit.Views&version=1.0.0
#tool nuget:?package=JG.WebKit.Views&version=1.0.0
JG.WebKit.Views
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.
Features
- 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
Quick Start
Installation
dotnet add package JG.WebKit.Views
Basic Usage
using 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);
Template Syntax
Variables
{{ title }}
{{{ rawHtml }}}
{{ user.name }}
{{ items[0] }}
{{ user.profile.settings }}
Conditionals
{{#if loggedIn }}
Welcome back, {{ user.name }}!
{{#elseif isGuest }}
Welcome, guest!
{{#else}}
Please log in.
{{/if}}
{{#if count > 10 }}Many items{{/if}}
{{#if age >= 18 }}Adult{{/if}}
{{#if status == "active" }}Active{{/if}}
{{#if !isEmpty }}Has content{{/if}}
Loops
{{#each posts as post }}
<article>
<h2>{{ post.title }}</h2>
<p>Item {{ index }} of {{ posts.length }}</p>
</article>
{{#empty}}
<p>No posts available.</p>
{{/each}}
{{#each categories as category }}
<h3>{{ category.name }}</h3>
{{#each category.items as item }}
<li>{{ item }}</li>
{{/each}}
{{/each}}
Partials
{{> header }}
{{> card "featured" }}
{{> userCard user }}
<div class="user-card">
<h3>{{ name }}</h3>
<p>{{ email }}</p>
</div>
Layouts
{{#layout "main" }}
<h1>{{ title }}</h1>
<p>Page content here</p>
{{#section "sidebar" }}
<aside>Custom sidebar</aside>
{{/section}}
<html>
<head><title>{{ title }}</title></head>
<body>
<nav>Site navigation</nav>
<main>
{{#yield "content" }}
</main>
<aside>
{{#yield-default "sidebar" }}
<p>Default sidebar</p>
{{/yield-default}}
</aside>
</body>
</html>
Helpers
{{ date publishedAt "yyyy-MM-dd" }}
{{ truncate description 100 }}
{{ uppercase title }}
{{ lowercase email }}
{{ json data }}
{{ default title "Untitled Page" }}
{{ ifval user.isAdmin "Admin" "User" }}
{{ concat firstName " " lastName }}
{{ replace slug "-" " " }}
{{ count items }}
<link rel="stylesheet" href="{{ css "styles.css" }}">
<script src="{{ js "app.js" }}"></script>
<img src="{{ img "logo.png" }}" alt="Logo">
Comments
{{-- This is a comment and won't be rendered --}}
{{-- {{ debugVariable }} --}}
Configuration
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
};
Advanced Features
Custom Helpers
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 }}
Global Data
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 }}
Cache Invalidation
// Invalidate specific template
await engine.InvalidateCacheAsync("blog/post");
// Clear entire cache
await engine.InvalidateAllAsync();
Hot Reload (Development)
var provider = new FileTemplateProvider(
"./templates",
enableHotReload: true // Auto-reload on file changes
);
Performance
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
Real-World Examples
Blog Post Page
// 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
{{#layout "main" }}
<article class="post">
<header>
<h1>{{ post.title }}</h1>
<time>{{ date post.publishedAt "MMMM dd, yyyy" }}</time>
<span>By {{ post.author }}</span>
</header>
<div class="content">
{{{ post.bodyHtml }}}
</div>
<footer>
{{#if post.tags }}
<div class="tags">
{{#each post.tags as tag }}
<span class="tag">{{ tag }}</span>
{{/each}}
</div>
{{/if}}
</footer>
</article>
<section class="comments">
<h2>Comments ({{ comments.length }})</h2>
{{#each comments as comment }}
{{> comment comment }}
{{#empty}}
<p>No comments yet. Be the first!</p>
{{/each}}
</section>
{{#section "sidebar" }}
<aside>
<h3>Related Posts</h3>
{{#each relatedPosts as related }}
{{> postCard related }}
{{/each}}
</aside>
{{/section}}
Email Templates
// 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
{{#layout "email-base" }}
<h1>Welcome, {{ user.name }}!</h1>
<p>Thank you for joining our platform.</p>
<p>Please verify your email address:</p>
<a href="{{ verificationLink }}" class="button">Verify Email</a>
API Response Rendering
// 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");
}
Documentation
- 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
Testing
# Run all tests
dotnet test
# Run with coverage
dotnet test /p:CollectCoverage=true
Test Coverage: 175 tests, 100% pass rate, <1s execution time
Contributing
Contributions welcome! Please read CONTRIBUTING.md for guidelines.
License
Apache License 2.0 - see LICENSE for details.
Support
- Report Issues
- Discussions
- Email: support@example.com
Acknowledgments
Built with ❤️ for the .NET community. Inspired by Handlebars, Liquid, and Razor syntax.
Version: 1.0.0 | Released: March 6, 2026 | Status: Production Ready
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net8.0
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.0.0 | 85 | 3/6/2026 |