microsoft/migrating-razorengine-to-razorlight
> Migrates the deprecated RazorEngine to RazorLight for Razor template rendering outside of MVC. Use ONLY when RazorEngine has been flagged as deprecated or unmaintained and must be replaced — not for version-bump scenarios where RazorEngine is still supported.
npx skills add https://github.com/microsoft/upgrade-agent-plugins --skill migrating-razorengine-to-razorlight
Migrate Razor-based template rendering from RazorEngine to RazorLight. RazorEngine relies on a global static Engine.Razor singleton and synchronous compilation, which is incompatible with modern .NET. RazorLight provides an async-first API, built-in dependency injection support, and runs on .NET 6+. The core change is replacing Engine.Razor.RunCompile() with RazorLightEngine.CompileRenderAsync().
<PackageReference Include="RazorEngine" Version="*" />
<!-- Also remove if present -->
<PackageReference Include="RazorEngine.NetCore" Version="*" />
<PackageReference Include="RazorLight" Version="{latest-stable}" />
Migration Progress:
- [ ] Step 1: Detect RazorEngine usage
- [ ] Step 2: Update package references
- [ ] Step 3: Replace engine initialization
- [ ] Step 4: Migrate template rendering calls
- [ ] Step 5: Migrate custom template sources
- [ ] Step 6: Update caching logic
- [ ] Step 7: Build and verify
Scan the project for:
using RazorEngine; and using RazorEngine.Templating; statementsEngine.Razor.RunCompile(), Engine.Razor.Compile(), Engine.Razor.Run()ITemplateSource or ITemplateResolver implementationsTemplateServiceConfiguration setup codeRemove RazorEngine (or RazorEngine.NetCore) and add RazorLight in the project file.
| Old (RazorEngine) | New (RazorLight) |
|-------------------|------------------|
| Engine.Razor (global static, no setup) | new RazorLightEngineBuilder().UseMemoryCachingProvider().Build() |
| var config = new TemplateServiceConfiguration(); var service = RazorEngineService.Create(config); | new RazorLightEngineBuilder().UseEmbeddedResourcesProject(typeof(MyClass)).Build() |
Register the engine in DI as a singleton because RazorLightEngine is thread-safe and caches compiled templates:
services.AddSingleton<IRazorLightEngine>(sp =>
new RazorLightEngineBuilder()
.UseMemoryCachingProvider()
.Build());
All RazorLight rendering methods are async. Update call sites to use await.
| Old (RazorEngine) | New (RazorLight) |
|-------------------|------------------|
| Engine.Razor.RunCompile(templateSource, "key", typeof(T), model) | await engine.CompileRenderStringAsync("key", templateSource, model) |
| Engine.Razor.Compile(templateSource, "key") then Engine.Razor.Run("key", typeof(T), model) | await engine.CompileRenderStringAsync("key", templateSource, model) |
| Engine.Razor.IsTemplateCached("key", typeof(T)) | Check via engine.Options.CachingProvider.RetrieveTemplate("key") |
The typeof(T) model-type parameter is not needed in RazorLight — the engine infers the model type automatically.
If the project uses custom template resolution (loading templates from a database, embedded resources, or custom paths), implement RazorLight's RazorLightProject:
| Old (RazorEngine) | New (RazorLight) |
|-------------------|------------------|
| ITemplateResolver | Inherit from RazorLightProject |
| ITemplateSource | Return RazorLightProjectItem from your project |
Configure the engine to use the custom project:
var engine = new RazorLightEngineBuilder()
.UseProject(new MyCustomProject())
.UseMemoryCachingProvider()
.Build();
For file-based templates, use the built-in file project:
var engine = new RazorLightEngineBuilder()
.UseFileSystemProject("/path/to/templates")
.Build();
RazorLight handles caching internally via ICachingProvider. Remove any manual template caching code that was needed for RazorEngine. The MemoryCachingProvider (default) caches compiled templates in memory automatically.
dotnet build
RazorLight compiles templates at runtime using the Razor SDK. Ensure the project targets a framework that includes the Razor runtime components. If targeting a worker service or console app, add <PreserveCompilationContext>true</PreserveCompilationContext> to the project file.
RazorLight uses dynamic models differently than RazorEngine. If using anonymous types, pass them as ExpandoObject instances or use strongly-typed models.
RazorLightEngine is thread-safe and should be registered as a singleton. Creating a new engine per request causes excessive compilation overhead and memory pressure.
During development, the memory caching provider does not detect source changes. Clear the cache or restart the application to pick up template edits.
Take microsoft/migrating-razorengine-to-razorlight from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.