Lightweight Razor-based templates for ASP.NET Core without MVC, Razor Pages, or Blazor, optimized for high-performance, unbuffered rendering with low allocations. Compatible with trimming and native AOT. Great for returning dynamically rendered HTML from Minimal APIs, middleware, etc. Supports .NET 8+
-
Install the NuGet package into your ASP.NET Core project (.NET 8+):
> dotnet add package RazorSlices
-
Create a directory in your project called Slices and add a _ViewImports.cshtml file to it with the following content:
@inherits RazorSliceHttpResult @using System.Globalization; @using Microsoft.AspNetCore.Razor; @using Microsoft.AspNetCore.Http.HttpResults; @tagHelperPrefix __disable_tagHelpers__: @removeTagHelper *, Microsoft.AspNetCore.Mvc.Razor
-
In the same directory, add a Hello.cshtml file with the following content:
@inherits RazorSliceHttpResult<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>
Each .cshtml file will have a proxy type generated for it by the Razor Slices source generator that you can use as the generic argument to the various APIs in Razor Slices for rendering slices.
-
Add a minimal API to return the slice in your Program.cs:
app.MapGet("/hello", () => Results.Extensions.RazorSlice<MyApp.Slices.Hello, DateTime>(DateTime.Now));
This package is currently available from nuget.org:
> dotnet add package RazorSlices
If you wish to use builds from this repo's main
branch you can install them from this repo's package feed.
-
Create a personal access token for your GitHub account with the
read:packages
scope with your desired expiration length: -
At the command line, navigate to your user profile directory and run the following command to add the package feed to your NuGet configuration, replacing the
<GITHUB_USER_NAME>
and<PERSONAL_ACCESS_TOKEN>
placeholders with the relevant values:~> dotnet nuget add source -n GitHub -u <GITHUB_USER_NAME> -p <PERSONAL_ACCESS_TOKEN> https://nuget.pkg.github.com/DamianEdwards/index.json
-
You should now be able to add a reference to the package specifying a version from the repository packages feed
-
See these instructions for further details about working with GitHub package feeds.
The library is still new and features are being actively added.
-
ASP.NET Core 8.0 and above
-
Strongly-typed models (via
@inherits RazorSlice<MyModel>
or@inherits RazorSliceHttpResult<MyModel>
) -
Razor constructs:
-
Implicit expressions, e.g.
@someVariable
-
Explicit expressions, e.g.
@(someBool ? thisThing : thatThing)
-
Control structures, e.g.
@if()
,@switch()
, etc. -
Looping, e.g.
@for
,@foreach
,@while
,@do
-
Code blocks, e.g.
@{ var someThing = someOtherThing; }
-
Functions, e.g.
@functions { private readonly string _someString = "A very important string"; private int DoAThing() => 123; }
-
Templated Razor delegates, e.g.
@inherits RazorSlice<Todo> <h1>@Title(Model)</h1> @functions { private IHtmlContent Title(Todo todo) { <text>Todo @todo.Id: @todo.Title</text> return HtmlString.Empty; } }
-
-
DI-activated properties via
@inject
-
Rendering slices from slices (aka partials) via
@(await RenderPartialAsync<MyPartial>())
-
Using slices as layouts for other slices, including layouts with strongly-typed models:
-
For the layout slice, inherit from
RazorLayoutSlice
orRazorLayoutSlice<TModel>
and use@await RenderBodyAsync()
in the layout to render the body@inherits RazorLayoutSlice<LayoutModel> <!DOCTYPE html> <html lang="en"> <head> <title>@Model.Title</title> @await RenderSectionAsync("head") </head> <body> @await RenderBodyAsync() <footer> @await RenderSectionAsync("footer") </footer> </body> </html>
-
For the slice using the layout, implement
IUsesLayout<TLayout>
orIUsesLayout<TLayout, TModel>
to declare which layout to use. If using a layout with a model, ensure you implement theLayoutModel
property in your@functions
block, e.g@inherits RazorSlice<SomeModel> @implements IUsesLayout<LayoutSlice, LayoutModel> <div> @* Content here *@ </div> @functions { public LayoutModel LayoutModel => new() { Title = "My Layout" }; }
-
Layouts can render sections via
@await RenderSectionAsync("SectionName")
and slices can render content into sections by overridingExecuteSectionAsync
, e.g.:protected override Task ExecuteSectionAsync(string name) { if (name == "lorem-header") { <p class="text-info">This page renders a custom <code>IHtmlContent</code> type that contains lorem ipsum content.</p> } return Task.CompletedTask; }
Note: The
@section
directive is not supported as it's incompatible with the rendering approach of Razor Slices
-
-
Asynchronous rendering, i.e. the template can contain
await
statements, e.g.@await WriteTheThing()
-
Writing UTF8
byte[]
values directly to the output -
Rendering directly to
PipeWriter
,Stream
,TextWriter
,StringBuilder
, andstring
outputs, including optimizations for not boxing struct values, zero-allocation rendering of primitives like numbers, etc. (rather than just callingToString()
on everything) -
Return a slice instance directly as an
IResult
in minimal APIs via@inherits RazorSliceHttpResult
andResults.Extensions.RazorSlice("/Slices/Hello.cshtml")
-
Full support for trimming and native AOT when used in conjunction with ASP.NET Core Minimal APIs
- Extensions to help support using HTMX with Razor Slices
- Getting small updates to the Razor compiler itself to get some usability and performance improvements, e.g.:
- Don't mark the template's
ExecuteAsync
method as anasync
method unless the template containsawait
statements to save on the async state machine overhead - Support compiling static template elements to UTF8 string literals (
ReadOnlySpan<byte>
) instead of string literals to save on the UTF16 to UTF8 conversion during rendering - Support disabling the default registered
@addtaghelper
and@model
directives which rely on MVC
- Don't mark the template's
- Tag Helpers and View Components (they're tied to MVC and are intrinsically "heavy")
@model
directive (the Razor compiler does not support its use in conjunction with custom base-types via@inherits
)@attribute [Authorize]
(wrong layer of abstraction for minimal APIs, etc.)@section
directive (the Razor compiler emits code that is incompatible with the rendering approach of Razor Slices)