├── .gitattributes
├── README.md
├── RazorFun
├── User.cs
├── Header.razor
├── appsettings.Development.json
├── appsettings.json
├── RazorFun.csproj
├── Templates.razor
├── Program.cs
└── Properties
│ └── launchSettings.json
├── RazorProgramMain
├── appsettings.Development.json
├── appsettings.json
├── RazorProgramMain.csproj
├── Program.razor
└── Properties
│ └── launchSettings.json
├── RazorTemplates.slnx
├── RazorTemplates
├── RazorTemplates.csproj
├── HtmlResultsExtensions.cs
├── RenderFragmentExtensions.cs
└── FragmentComponent.cs
├── LICENSE
└── .gitignore
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Razor Templates
2 |
3 | Experiment creating templating extensions for Razor Components.
4 |
--------------------------------------------------------------------------------
/RazorFun/User.cs:
--------------------------------------------------------------------------------
1 | namespace RazorFun;
2 |
3 | public class User
4 | {
5 | public required string Name { get; set; }
6 | public int Age { get; set; }
7 | }
8 |
--------------------------------------------------------------------------------
/RazorFun/Header.razor:
--------------------------------------------------------------------------------
1 |
4 |
5 | @code {
6 | [Parameter]
7 | public required string Text { get; set; }
8 | }
9 |
--------------------------------------------------------------------------------
/RazorFun/appsettings.Development.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Information",
5 | "Microsoft.AspNetCore": "Warning"
6 | }
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/RazorProgramMain/appsettings.Development.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Information",
5 | "Microsoft.AspNetCore": "Warning"
6 | }
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/RazorFun/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Information",
5 | "Microsoft.AspNetCore": "Warning"
6 | }
7 | },
8 | "AllowedHosts": "*"
9 | }
10 |
--------------------------------------------------------------------------------
/RazorProgramMain/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Information",
5 | "Microsoft.AspNetCore": "Warning"
6 | }
7 | },
8 | "AllowedHosts": "*"
9 | }
10 |
--------------------------------------------------------------------------------
/RazorTemplates.slnx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/RazorTemplates/RazorTemplates.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net10.0
5 | enable
6 | enable
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/RazorFun/RazorFun.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net10.0
5 | enable
6 | enable
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/RazorProgramMain/RazorProgramMain.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net10.0
5 | enable
6 | enable
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/RazorProgramMain/Program.razor:
--------------------------------------------------------------------------------
1 | @using Microsoft.AspNetCore.Components
2 |
3 | @code
4 | {
5 | public static void Main(string[] args)
6 | {
7 | var builder = WebApplication.CreateBuilder(args);
8 | builder.Services.AddRazorComponents();
9 | var app = builder.Build();
10 |
11 | app.MapGet("/", () => Results.Razor(Home()));
12 |
13 | app.Run();
14 | }
15 |
16 | private static RenderFragment Home()
17 | {
18 | return @
19 | Welcome to a single-file Razor application!
20 |
;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/RazorFun/Templates.razor:
--------------------------------------------------------------------------------
1 | @code {
2 | public static RenderFragment Home() =>
3 | (
4 | @
5 | Welcome to the RazorFun application!
6 | Template rendered directly to a string
7 | Template rendered via IResult
8 |
9 | );
10 |
11 | public static RenderFragment Card(User user) =>
12 | (
13 | @
14 |
15 |
Age: @user.Age
16 |
17 | );
18 | }
19 |
--------------------------------------------------------------------------------
/RazorFun/Program.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Components;
2 | using RazorFun;
3 |
4 | var builder = WebApplication.CreateBuilder(args);
5 |
6 | builder.Services.AddRazorComponents();
7 |
8 | var app = builder.Build();
9 |
10 | app.MapGet("/", () => Results.Razor(Templates.Home()));
11 |
12 | app.MapGet("/string", async (IServiceProvider services, string name = "World") =>
13 | {
14 | var user = new User { Name = name, Age = 32 };
15 | return await Templates.Card(user).RenderAsync(services);
16 | });
17 |
18 | app.MapGet("/iresult", (string name = "World") =>
19 | {
20 | var user = new User { Name = name, Age = 32 };
21 | return Results.Razor(Templates.Card(user));
22 | });
23 |
24 | app.Run();
25 |
--------------------------------------------------------------------------------
/RazorTemplates/HtmlResultsExtensions.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Components;
2 | using Microsoft.AspNetCore.Http.HttpResults;
3 | using RazorTemplates;
4 |
5 | namespace Microsoft.AspNetCore.Http;
6 |
7 | public static partial class HtmlResultsExtensions
8 | {
9 | extension(Results)
10 | {
11 | public static RazorComponentResult Razor(RenderFragment fragment, int? statusCode = null, string? contentType= null)
12 | {
13 | return new RazorComponentResult(new FragmentComponent.ParametersDictionary(fragment))
14 | {
15 | StatusCode = statusCode,
16 | ContentType = contentType,
17 | PreventStreamingRendering = true
18 | };
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/RazorFun/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json.schemastore.org/launchsettings.json",
3 | "profiles": {
4 | "http": {
5 | "commandName": "Project",
6 | "dotnetRunMessages": true,
7 | "launchBrowser": true,
8 | "applicationUrl": "http://razortemplates.dev.localhost:5024",
9 | "environmentVariables": {
10 | "ASPNETCORE_ENVIRONMENT": "Development"
11 | }
12 | },
13 | "https": {
14 | "commandName": "Project",
15 | "dotnetRunMessages": true,
16 | "launchBrowser": true,
17 | "applicationUrl": "https://razortemplates.dev.localhost:7008;http://razortemplates.dev.localhost:5024",
18 | "environmentVariables": {
19 | "ASPNETCORE_ENVIRONMENT": "Development"
20 | }
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/RazorProgramMain/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json.schemastore.org/launchsettings.json",
3 | "profiles": {
4 | "http": {
5 | "commandName": "Project",
6 | "dotnetRunMessages": true,
7 | "launchBrowser": true,
8 | "applicationUrl": "http://razorprogrammain.dev.localhost:5211",
9 | "environmentVariables": {
10 | "ASPNETCORE_ENVIRONMENT": "Development"
11 | }
12 | },
13 | "https": {
14 | "commandName": "Project",
15 | "dotnetRunMessages": true,
16 | "launchBrowser": true,
17 | "applicationUrl": "https://razorprogrammain.dev.localhost:7281;http://razorprogrammain.dev.localhost:5211",
18 | "environmentVariables": {
19 | "ASPNETCORE_ENVIRONMENT": "Development"
20 | }
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2025 Damian Edwards
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/RazorTemplates/RenderFragmentExtensions.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Components.RenderTree;
2 | using Microsoft.AspNetCore.Components.Web;
3 | using Microsoft.Extensions.DependencyInjection;
4 | using Microsoft.Extensions.Logging;
5 | using RazorTemplates;
6 |
7 | namespace Microsoft.AspNetCore.Components;
8 |
9 | public static class RenderFragmentExtensions
10 | {
11 | extension(RenderFragment fragment)
12 | {
13 | public async Task RenderAsync(IServiceProvider services, HtmlRenderer renderer)
14 | {
15 | var loggerFactory = services.GetRequiredService();
16 | return await RenderImpl(fragment, renderer);
17 | }
18 |
19 | public async Task RenderAsync(IServiceProvider services)
20 | {
21 | var loggerFactory = services.GetRequiredService();
22 | using var renderer = new HtmlRenderer(services, loggerFactory);
23 | return await RenderImpl(fragment, renderer);
24 | }
25 | }
26 |
27 | private static async Task RenderImpl(RenderFragment fragment, HtmlRenderer renderer)
28 | {
29 | var parameters = new FragmentComponent.ParametersDictionary(fragment);
30 | var parameterView = ParameterView.FromDictionary(parameters);
31 | var root = await renderer.Dispatcher.InvokeAsync(() => renderer.RenderComponentAsync(parameterView));
32 | await root.QuiescenceTask;
33 | return await renderer.Dispatcher.InvokeAsync(root.ToHtmlString);
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/RazorTemplates/FragmentComponent.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Components;
2 | using Microsoft.AspNetCore.Components.Rendering;
3 |
4 | namespace RazorTemplates;
5 |
6 | internal class FragmentComponent : ComponentBase
7 | {
8 | [Parameter]
9 | public required RenderFragment RenderFragment { get; set; }
10 |
11 | protected override void BuildRenderTree(RenderTreeBuilder builder)
12 | {
13 | builder.AddContent(0, RenderFragment);
14 | }
15 |
16 | public readonly struct ParametersDictionary(in RenderFragment fragment)
17 | : IReadOnlyDictionary, IDictionary
18 | {
19 | private static readonly ICollection _keys = [nameof(RenderFragment)];
20 |
21 | private readonly RenderFragment _fragment = fragment;
22 |
23 | public IEnumerable Keys => _keys;
24 | public IEnumerable