Razor Slices: Render Razor Views in ASP.NET Core Minimal APIs
Razor Slices is a lightweight, AOT-friendly Razor template engine for ASP.NET Core Minimal APIs. Here's how to set it up, use layouts and partials, and when to reach for it.
With .NET 8 and beyond, Minimal APIs have continued to grow, slowly providing a feature set similar to the frameworks before them, including ASP.NET Core MVC and Razor Pages. I was surprised when someone in the community (👋 Hi Johnathan Channon) pointed me to a fascinating Razor Slices project. Razor Slices is written by Damian Edwards, an ASP.NET Core team member with a long history of working on the Razor view engine. His goal is to bring Razor functionality to Minimal API adopters in a similar way that ASP.NET Core MVC, Razor Pages, and Blazor users enjoy today.
Razor Slices is a lightweight, high-performance Razor template engine for ASP.NET Core. It renders HTML directly from Minimal APIs, middleware, and other endpoints, with no MVC, Razor Pages, or Blazor. Each .cshtml file compiles to a strongly-typed slice you render with a generated Create() method, and it supports trimming and native AOT.
Updated: October 2026 — this post originally covered Razor Slices 0.7.0. The library changed a lot since 2023. The API is now strongly typed through a source generator, and partials, layouts, and native AOT all work. I’ve rewritten the examples and the pros and cons to match the current release.
In this post, we’ll see how to set up Razor Slices in your ASP.NET Core applications and think about the pros and cons of its usage. If you want a refresher on the view engine itself first, see What Is Razor.
Getting Started with Razor Slices
In a Minimal API project targeting .NET 8 or later, start by installing the Razor Slices package. You can do this with the dotnet add package RazorSlices command or by adding the appropriate PackageReference in your .csproj file.
<PackageReference Include="RazorSlices" Version="0.12.2" />Next, create a folder at the root of your ASP.NET Core application. It can be named anything you like. I chose Slices as it was what the documentation recommended. This directory is where you’ll place the Razor views you want to render from your Minimal API endpoints.
Within the Slices folder, create a new file named _ViewImports.cshtml. The view imports file lets you create a shared context for all views within the folder, including namespaces, base classes, and tag helpers. Here’s the contents of my file.
@inherits RazorSlice
@using System.Globalization;@using Microsoft.AspNetCore.Razor;@using Microsoft.AspNetCore.Http.HttpResults;@using RazorSlices;
@tagHelperPrefix __disable_tagHelpers__:@removeTagHelper *, Microsoft.AspNetCore.Mvc.RazorYou’ll notice two strange things when looking at this file:
- The
@inheritsvalue isRazorSlice, the base class every slice derives from. (Earlier versions usedRazorSliceHttpResult; that’s no longer how you wire things up.) - Tag helpers are removed and obfuscated. That’s because they’re not supported in this model. If you’re porting a Razor Pages implementation over to Razor Slices, expect to make some modifications or concessions.
Next, you’ll want to create a new Hello.cshtml view in the Slices folder.
@inherits RazorSlice<DateTime><!DOCTYPE html><html lang="en"><head> <meta charset="utf-8"> <title>Hello from Razor Slices!</title></head><body><p> Hello from Razor Slices! The time is @Model</p></body></html>Note that the view also uses the @inherits directive, but this time with a generic type argument. The generic type is the type of the @Model instance.
The biggest change since 2023 is the source generator. It inspects each .cshtml file and emits a strongly-typed proxy for it. The proxy exposes a Create method whose signature matches your model:
- Slices with a model get
Create(ModelType model), e.g.MyApp.Slices.Hello.Create(DateTime.Now) - Slices without a model get a parameterless
Create()
That gives you compile-time validation that you’re passing the right model type, with no magic strings. Because a slice implements IResult, you can return it directly from a Minimal API route handler.
var builder = WebApplication.CreateBuilder(args);var app = builder.Build();
app.MapGet("/", () => MyApp.Slices.Hello.Create(DateTime.Now));
app.Run();And that’s it! You are now rendering Razor views through your Minimal API endpoint, with full IntelliSense and compile-time checking on the model.
The Pros of Razor Slices
The most obvious benefit is that you can use Razor to generate HTML results in your Minimal API endpoints. I might be biased, but I love the Razor syntax and the benefits it brings to the web development experience.
Your Razor views can also use the @inject directive to pull dependencies into the rendering process.
Performance is a headline feature. Razor Slices renders unbuffered with low allocations, writes primitives like numbers without boxing or calling ToString() on everything, and can render straight to a PipeWriter, Stream, TextWriter, StringBuilder, or string. It also has full support for trimming and native AOT with Minimal APIs. That was only “planned” when I first wrote this post in 2023, and it has since shipped.
What Razor Slices Can Do Now
Older tutorials, including the first version of this one, make Razor Slices look bare-bones. It has grown a full set of composition features since then.
Partials. You can render a slice from within another slice, which is how you compose and componentize views.
@(await RenderPartialAsync<MyPartial>())Layouts and sections. Layouts are supported through RazorLayoutSlice (or RazorLayoutSlice<TModel>). The layout renders the child with @await RenderBodyAsync() and can expose named sections.
@inherits RazorLayoutSlice<LayoutModel><!DOCTYPE html><html lang="en"><head> <title>@Model.Title</title> @await RenderSectionAsync("head")</head><body> @await RenderBodyAsync()</body></html>A slice opts into a layout by implementing IUsesLayout<TLayout> (or IUsesLayout<TLayout, TModel>). The MVC-style @section directive isn’t supported because it conflicts with how slices render, so sections run through RenderSectionAsync and ExecuteSectionAsync instead.
Minimal slices. For templates where performance matters most and you don’t need injection, layouts, or partials, inherit RazorSliceBase / RazorSliceBase<TModel> for an even leaner path.
Shareable templated helpers. You can define reusable markup in a public interface, implement it in a .cshtml file, and call it as @Helpers.UiHelpers.StatusBadge(...), even across projects through a Razor class library. (Helpers require C# 14.)
The Cons of Razor Slices
Razor Slices was never meant to be a one-to-one replacement for MVC or Razor Pages, so a few features are off the table by design. It skips Tag Helpers and View Components (both tied to MVC and heavy), the @model directive (the Razor compiler can’t use it alongside a custom @inherits base type), and the @section and @attribute [Authorize] directives.
A new paradigm means leaving behind the old one. If you depend on third-party Razor libraries built for MVC or Razor Pages, they likely won’t work here.
Some newer conveniences, like shareable helpers, require C# 14, so check your SDK and LangVersion before reaching for them.
Conclusion
Razor Slices is worth checking out if you want to render HTML a big step above string interpolation in your API endpoints. You get the Razor syntax you love, expressions, control structures, looping, and code blocks, plus partials, layouts, shareable helpers, and native AOT, all without dragging in MVC. For most HTML-rendering needs in a Minimal API, that’s more than enough. Go in knowing it’s intentionally not a drop-in MVC replacement, and that’s OK.
If you need interactive components instead of plain HTML, look at returning Blazor components with RazorComponentResult. And if you’re templating text outside of HTTP responses entirely, generating output with the Razor engine is another option.
Check out the library at Razor Slices and let me know your thoughts. As always, thanks for reading and sharing my blog posts.
Around the web
Comments
Loading comments from GitHub Discussions…