Important
This repository is an independently maintained continuation of toddams/RazorLight. It is no longer maintained as a contribution fork, so compatibility and release policy may diverge. See UPSTREAM.md for provenance and the upstream synchronization policy.
Note
The maintained release line targets .NET 10 and is published as Dijgrid.RazorLight, beginning
with version 3.0.0. The RazorLight 2.3.1 package is the historical upstream build rather than
this continuation.
Use Razor to build templates from files, embedded resources, strings, databases, or a custom source outside ASP.NET MVC. The maintained source and samples support .NET 10. See framework support, the dependency policy, and testing guidance for the current maintenance baseline.
- Quickstart
- Compatibility and support
- Template sources
- Includes and partial templates
- Encoding
- Additional metadata references
- Enable IntelliSense support
- FAQ
- Project maintenance
Install the .NET 10 SDK selected by global.json, then add the independently
maintained package:
dotnet add package Dijgrid.RazorLight --version 3.0.0Do not use the historical RazorLight 2.3.1 package as evidence of this continuation's framework
or dependency baseline.
The simplest scenario creates a template from a string. Each template has a templateKey, allowing
RazorLight to cache and reuse its compiled form. String templates do not require a project; layouts,
includes, and project-based template lookup do.
var engine = new RazorLightEngineBuilder()
.UseMemoryCachingProvider()
.Build();
string template = "Hello, @Model.Name. Welcome to RazorLight repository";
ViewModel model = new ViewModel {Name = "John Doe"};
string result = await engine.CompileRenderStringAsync("templateKey", template, model);To render a compiled template:
var cacheResult = engine.Handler.Cache.RetrieveTemplate("templateKey");
if(cacheResult.Success)
{
var templatePage = cacheResult.Template.TemplatePageFactory();
string result = await engine.RenderTemplateAsync(templatePage, model);
}- Maintained source, tools, tests, and samples target .NET 10 only.
- Moving from the historical
2.3.1package is a framework-breaking migration; readdocs/framework-support.mdbefore upgrading. - The public API and historical behavior baseline are recorded in
docs/compatibility-baseline.md. - Current model, import, LINQ, and template-cache behavior is recorded in the template language compatibility matrix.
- Azure Functions v4 is build-validated by the maintained sample. AWS Lambda and other hosting environments are not part of CI and should be treated as community-supported until a focused integration fixture is added.
- For support and security reporting, follow
SUPPORT.mdandSECURITY.md.
RazorLight has built-in providers for file-system and embedded-resource templates. Implement
RazorLightProject to load templates from another source, such as a database.
Project-backed templates receive RazorLight's built-in imports and namespaces configured with
AddDefaultNamespaces. String templates currently require explicit @using directives. See the
template language compatibility matrix for the tested
behavior and known dynamic-model limitations.
For a file-system project, the template key is a path relative to the root directory passed to
RazorLightEngineBuilder.
var engine = new RazorLightEngineBuilder()
.UseFileSystemProject("C:/RootFolder/With/YourTemplates")
.UseMemoryCachingProvider()
.Build();
var model = new {Name = "John Doe"};
string result = await engine.CompileRenderAsync("Subfolder/View.cshtml", model);For an embedded resource, the template key combines the resource namespace and template file name.
The examples below use this project structure:
Project/
Model.cs
Program.cs
Project.csproj
Project.Core/
EmailTemplates/
Body.cshtml
Project.Core.csproj
SomeService.cs
var engine = new RazorLightEngineBuilder()
.UseEmbeddedResourcesProject(typeof(SomeService).Assembly)
.UseMemoryCachingProvider()
.Build();
var model = new Model();
string html = await engine.CompileRenderAsync("EmailTemplates.Body", model);Setting the root namespace lets you omit that prefix from the template key:
var engine = new RazorLightEngineBuilder()
.UseEmbeddedResourcesProject(typeof(SomeService).Assembly, "Project.Core.EmailTemplates")
.UseMemoryCachingProvider()
.Build();
var model = new Model();
string html = await engine.CompileRenderAsync("Body", model);To store templates in a database or another custom location, implement RazorLightProject. The
project resolves template content and imports, and RazorLight also uses it to find layouts and
included templates.
var project = new EntityFrameworkRazorProject(new AppDbContext());
var engine = new RazorLightEngineBuilder()
.UseProject(project)
.UseMemoryCachingProvider()
.Build();
// For key as a GUID
string guidResult = await engine.CompileRenderAsync(
"6cc277d5-253e-48e0-8a9a-8fe3cae17e5b",
new { Name = "John Doe" });
// Or integer
int templateKey = 322;
string integerResult = await engine.CompileRenderAsync(
templateKey.ToString(),
new { Name = "John Doe" });See the custom project sample for a complete implementation.
Includes let templates share smaller, reusable components. They reduce duplication and keep complex templates manageable.
Includes require a RazorLight project so the engine can locate the referenced template.
@model MyProject.TestViewModel
<div>
Hello @Model.Title
</div>
@{ await IncludeAsync("SomeView.cshtml", Model); }The first argument is the template key; the second is the model passed to the included template and
may be null.
RazorLight HTML-encodes model values by default. Use Raw when a specific value is already safe to
render without encoding.
/* With encoding (default) */
string encodedTemplate = "Render @Model.Tag";
string encodedResult = await engine.CompileRenderStringAsync(
"encoded",
encodedTemplate,
new { Tag = "<html>&" });
Console.WriteLine(encodedResult); // Output: <html>&
/* Without encoding */
string rawTemplate = "Render @Raw(Model.Tag)";
string rawResult = await engine.CompileRenderStringAsync(
"raw",
rawTemplate,
new { Tag = "<html>&" });
Console.WriteLine(rawResult); // Output: <html>&To disable encoding for an entire template, set DisableEncoding to true:
@model TestViewModel
@{
DisableEncoding = true;
}
<html>
Hello @Model.Tag
</html>Visual Studio assumes a Razor file is an ASP.NET MVC view. Add an explicit base class to help IntelliSense understand a RazorLight template:
@using RazorLight
@inherits TemplatePage<MyModel>
<html>
Your awesome template goes here, @Model.Name
</html>String templates work without configuring a project. The builder supplies NoRazorProject by
default, and the memory cache can store the compiled template:
var razorEngine = new RazorLightEngineBuilder()
.UseMemoryCachingProvider()
.Build();
string html = await razorEngine.CompileRenderStringAsync(
"welcome",
"Hello, @Model.Name!",
new { Name = "Ada" });Configure a file, embedded-resource, or custom project when templates use layouts, includes, or project keys. This behavior is covered by the quickstart smoke tests.
This isn't a RazorLight question, but please see this Stack Overflow answer.
This isn't a RazorLight question, but please look into PreMailer.Net.
Runtime compilation depends on metadata from the entry application. If rendering works during local development but fails after deployment, review the following common configuration issues.
RazorLight normally discovers metadata references from the entry assembly. When a required assembly is not discoverable, pass its metadata reference explicitly:
var metadataReference = MetadataReference.CreateFromFile("path-to-your-assembly");
var engine = new RazorLightEngineBuilder()
.UseMemoryCachingProvider()
.AddMetadataReferences(metadataReference)
.Build();RazorLight discovers metadata from the entry-point project's dependency context. Add this property to the entry-point project (for example, the web app, worker, or console app), not just a class library that wraps RazorLight:
<PropertyGroup>
<PreserveCompilationContext>true</PreserveCompilationContext>
</PropertyGroup>Set PreserveCompilationContext to true in the entry-point project's .csproj file:
<PropertyGroup>
<PreserveCompilationContext>true</PreserveCompilationContext>
</PropertyGroup>Self-contained, trimmed, and single-file deployments can remove assemblies required by runtime
compilation. Preserve the dependency context, avoid trimming template dependencies, and use
AddMetadataReferences when the host cannot expose a required reference automatically.
The repository build-validates a .NET 10 Azure Functions v4 isolated-worker sample. AWS Lambda, trimmed deployment, and dedicated ASP.NET Core integration-test hosting are not currently exercised in CI, so they are community-supported rather than declared broken. Keep template rendering behind an application service when you need to substitute it in broader host tests, and open a reproducible issue for host-specific failures.
- Read CONTRIBUTING.md before proposing or implementing changes.
- Report vulnerabilities privately according to SECURITY.md.
- Use SUPPORT.md to choose the appropriate issue type and diagnostic information.
- Review CHANGELOG.md for independent-maintenance changes.
- Follow the protected release process when preparing package artifacts or tags.
- Track accepted roadmap work in
.planfs, with working conventions documented in AGENTS.md.
README.md is generated from this file and the compile-checked snippets in
tests/RazorLight.Tests/Snippets. Regenerate it with:
dotnet build tests/RazorLight.Tests/RazorLight.Tests.csproj --configuration ReleaseCommit README.source.md, the snippet source, and the resulting README.md together. CI rebuilds
the documentation and fails if the generated file differs.
