From flexrender
Use when building FlexRender templates programmatically in C#, configuring FlexRenderBuilder, integrating via DI, or selecting NuGet packages. Covers AST classes, builder API, content parsers, resource loaders, and AOT-safe patterns.
How this skill is triggered — by the user, by Claude, or both
Slash command
/flexrender:template-csharpThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Help users build FlexRender templates programmatically in C#, configure the rendering pipeline, integrate with dependency injection, and select the right NuGet packages.
Help users build FlexRender templates programmatically in C#, configure the rendering pipeline, integrate with dependency injection, and select the right NuGet packages.
Ask what the user needs and recommend the minimal set:
| Scenario | Packages | Install |
|---|---|---|
| Parse YAML only | Core + Yaml | dotnet add package FlexRender.Core + dotnet add package FlexRender.Yaml |
| Render with Skia | + Skia | dotnet add package FlexRender.Skia |
| Render with ImageSharp | + ImageSharp | dotnet add package FlexRender.ImageSharp |
| SVG output | + Svg | dotnet add package FlexRender.Svg |
| QR codes | + QrCode | dotnet add package FlexRender.QrCode |
| Barcodes | + Barcode | dotnet add package FlexRender.Barcode |
| SVG elements | + SvgElement | dotnet add package FlexRender.SvgElement |
| HarfBuzz shaping | + HarfBuzz | dotnet add package FlexRender.HarfBuzz |
| HTTP resources | + Http | dotnet add package FlexRender.Http |
| DI integration | + DI | dotnet add package FlexRender.DependencyInjection |
| Everything | MetaPackage | dotnet add package FlexRender.MetaPackage |
| Backend | Features | Native Deps | Best For |
|---|---|---|---|
| Skia (default) | Full: QR, barcode, SVG elements, HarfBuzz, gradients | SkiaSharp native libs | Maximum features |
| ImageSharp | Pure .NET, QR/barcode (no SVG elements) | None | Cross-platform, no native deps |
| SVG | Vector output, QR/barcode/SVG elements | None | Scalable output, web |
using FlexRender;
// Minimal
using var render = new FlexRenderBuilder()
.WithSkia()
.Build();
// Full-featured
using var render = new FlexRenderBuilder()
.WithBasePath("./templates")
.WithHttpLoader(opts => {
opts.Timeout = TimeSpan.FromSeconds(60);
opts.MaxResourceSize = 20 * 1024 * 1024;
})
.WithLimits(limits => {
limits.MaxRenderDepth = 200;
limits.MaxTemplateFileSize = 2 * 1024 * 1024;
})
.WithFilter(new MyCustomFilter())
.WithSkia(skia => skia
.WithQr()
.WithBarcode()
.WithSvgElement())
.Build();
// Render from YAML file
byte[] png = await render.RenderFile("receipt.yaml", data);
// Render from YAML string
byte[] png = await render.RenderYaml(yamlString, data);
// Render from pre-parsed template
byte[] png = await render.Render(template, data);
services.AddFlexRender(builder => builder
.WithHttpLoader()
.WithSkia(skia => skia
.WithQr()
.WithBarcode()));
// Inject IFlexRender
public sealed class ReceiptService(IFlexRender render)
{
public async Task<byte[]> GenerateReceipt(ReceiptData data)
{
var templateData = new ObjectValue
{
["shopName"] = (StringValue)data.ShopName,
["items"] = new ArrayValue(data.Items.Select(i =>
new ObjectValue
{
["name"] = (StringValue)i.Name,
["price"] = (NumberValue)i.Price
})),
["total"] = (NumberValue)data.Total
};
return await render.RenderFile("receipt.yaml", templateData);
}
}
Build() can only be called once (_built flag prevents reuse)WithHttpLoader() wraps HttpClient creation in try-catchDispose() on IFlexRender disposes all registered resource loadersParse once, render many times with different data:
// At startup
var parser = new TemplateParser();
var template = await parser.ParseFileAsync("receipt.yaml");
// Per request
byte[] png = await render.Render(template, data);
The AST is immutable and thread-safe after parsing.
Build templates programmatically without YAML:
var template = new Template
{
Metadata = new TemplateMetadata { Name = "receipt", Version = 1 },
Canvas = new CanvasSettings
{
Fixed = FixedDimension.Width,
Width = 380,
Background = "#ffffff"
},
Layout =
[
new FlexElement
{
Padding = "24 20",
Gap = "12",
Children =
[
new TextElement
{
Content = "Hello, World!",
FontWeight = FontWeight.Bold,
Size = "1.5em",
Align = TextAlign.Center
},
new SeparatorElement
{
Style = SeparatorStyle.Dashed,
Color = "#cccccc"
},
new QrElement
{
Data = "https://example.com",
Size = 120,
ErrorCorrection = ErrorCorrectionLevel.M
}
]
}
]
};
byte[] png = await render.Render(template, data);
| Class | YAML type | Key Properties |
|---|---|---|
TextElement | text | Content, Font, FontFamily, FontWeight, FontStyle, Size, Color, Align, Wrap, Overflow, MaxLines, LineHeight |
FlexElement | flex | Direction, Wrap, Gap, ColumnGap, RowGap, Justify, Align, AlignContent, Overflow, Children |
ImageElement | image | Src, Fit |
QrElement | qr | Data, Size, ErrorCorrection, Foreground |
BarcodeElement | barcode | Data, Format, ShowText, Foreground |
SeparatorElement | separator | Orientation, Style, Thickness, Color |
TableElement | table | Array, As, Columns, Rows, HeaderFont, HeaderColor, HeaderSize, HeaderBackground |
SvgElement | svg | Src, Content, Fit |
ContentElement | content | Source, Format, Options |
EachElement | each | Array, As, Children |
IfElement | if | Condition, Then, ElseIf, Else + 13 comparison operators |
All elements inherit: Padding, Margin, Background, Opacity, BoxShadow, Rotate, Display, Grow, Shrink, Basis, AlignSelf, Order, Width, Height, MinWidth, MaxWidth, MinHeight, MaxHeight, Position, Top, Right, Bottom, Left, AspectRatio, TextDirection.
using FlexRender.Values;
// String
TemplateValue str = (StringValue)"Hello";
// Number
TemplateValue num = (NumberValue)42.5m;
// Boolean
TemplateValue flag = (BoolValue)true;
// Null
TemplateValue nil = NullValue.Instance;
// Array
var arr = new ArrayValue(
new ObjectValue { ["name"] = "Item 1" },
new ObjectValue { ["name"] = "Item 2" }
);
// Object (main data container)
var data = new ObjectValue
{
["name"] = "John",
["age"] = (NumberValue)30,
["active"] = (BoolValue)true,
["address"] = new ObjectValue
{
["city"] = "Moscow",
["zip"] = "101000"
},
["items"] = arr
};
public sealed class PercentFilter : ITemplateFilter
{
public string Name => "percent";
public TemplateValue Apply(TemplateValue input, FilterArguments arguments, CultureInfo culture)
{
if (input is not NumberValue number)
return input;
var decimals = arguments.Positional is NumberValue d ? (int)d.Value : 0;
return new StringValue(number.Value.ToString($"P{decimals}", culture));
}
}
// Register
var render = new FlexRenderBuilder()
.WithFilter(new PercentFilter())
.WithSkia()
.Build();
Usage in YAML: {{rate | percent:2}}
// Built-in parsers registered via packages:
// FlexRender.Content.Markdown -> "markdown"
// FlexRender.Content.Html -> "html"
// FlexRender.Content.Ndc -> "ndc"
// Custom parser
public sealed class MyParser : IContentParser
{
public string Format => "myformat";
public IReadOnlyList<TemplateElement> Parse(
string text,
ContentParserContext context,
IReadOnlyDictionary<string, object>? options = null)
{
return [new TextElement { Content = text }];
}
}
| Loader | Handles | Package |
|---|---|---|
FileResourceLoader | Relative file paths | Core |
Base64ResourceLoader | data: URIs | Core |
EmbeddedResourceLoader | embedded:// URIs | Core |
HttpResourceLoader | http://, https:// | Http |
builder.WithHttpLoader(opts => {
opts.Timeout = TimeSpan.FromSeconds(30);
opts.MaxResourceSize = 10 * 1024 * 1024;
});
builder.WithLimits(limits => {
limits.MaxTemplateFileSize = 1 * 1024 * 1024; // 1 MB (default)
limits.MaxDataFileSize = 10 * 1024 * 1024; // 10 MB
limits.MaxTemplateNestingDepth = 100;
limits.MaxRenderDepth = 100;
limits.MaxImageSize = 10 * 1024 * 1024;
limits.MaxFlexLines = 1000;
});
Never remove or weaken these limits.
var options = new RenderOptions
{
Format = ImageFormat.Png,
JpegQuality = 90,
Scale = 2.0f,
Culture = new CultureInfo("ru-RU"),
BmpColorMode = BmpColorMode.Monochrome1
};
byte[] result = await render.RenderFile("template.yaml", data, options);
FlexRender is fully AOT-compatible. When extending it:
Type.GetType(), no Activator.CreateInstance()dynamic — use pattern matching insteadsealed classes — all concrete classes must be sealedGeneratedRegex — use source-generated regex, not new Regex()ArgumentNullException.ThrowIfNull(param)IFlexRender disposes loadersswitch on concrete types// AOT-safe pattern matching
static string GetElementType(TemplateElement element) => element switch
{
TextElement => "text",
FlexElement => "flex",
ImageElement => "image",
_ => "unknown"
};
public sealed class ReceiptService : IDisposable
{
private readonly IFlexRender _render;
private readonly Template _template;
public ReceiptService()
{
_render = new FlexRenderBuilder()
.WithSkia(skia => skia.WithQr().WithBarcode())
.Build();
var parser = new TemplateParser();
_template = parser.ParseFileAsync("templates/receipt.yaml").Result;
}
public async Task<byte[]> Generate(Receipt receipt)
{
var data = new ObjectValue
{
["shopName"] = (StringValue)receipt.ShopName,
["address"] = (StringValue)receipt.Address,
["items"] = new ArrayValue(receipt.Items.Select(i =>
new ObjectValue
{
["name"] = (StringValue)i.Name,
["price"] = (NumberValue)i.Price,
["qty"] = (NumberValue)i.Quantity
})),
["total"] = (NumberValue)receipt.Total,
["paymentUrl"] = (StringValue)receipt.PaymentUrl
};
return await _render.Render(_template, data);
}
public void Dispose() => _render.Dispose();
}
npx claudepluginhub robonet/flexrender-marketplace --plugin flexrenderBuild responsive email templates with MJML in .NET apps using Mjml.Net. Compiles to cross-client HTML for Outlook, Gmail, Apple Mail with rendering and variables.
Guides selection of templating formats including Handlebars, Cookiecutter, Copier, Maven, and Harness via comparison matrix, workflows, and best practices for project scaffolding.
templ templating: syntax, components, attributes, styling, and JavaScript integration. Invoke when task involves any interaction with templ — writing .templ files, creating components, composing templates, testing rendered output, or understanding templ syntax.