# Using Razor Outside of Web

<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text">If you want to skip my mumbling and go straight to the coding part, jump to <code>Let's start</code>.</div>
</div>

Razor is a markup syntax for creating dynamic web pages in C#. It has been a part of the ASP.NET MVC framework since early 2011 and is currently a part of ASP.NET Core, serving as a templating engine for building web pages and components in MVC, Razor, and Blazor.

The primary idea behind Razor is to provide a simple syntax for generating HTML using a code-focused templating approach. The same approach can be seen in other popular frameworks and libraries focusing on HTML generation, such as React, Angular, Vue, etc. The described approach uses a hybrid of markup syntax (in this case, HTML) and a programming language (C# for Razor). It combines them during *rendering* to provide a hydrated and fully rendered web page or a page segment.

Let's get visual. Let's say we want to create a simple `<div>` with some populated data.

```xml
<div>
    <p><b>Name:</b> Denis</p>
    <p><b>Surname:</b> Ekart</p>
</div>
```

The same thing could be achieved in a `.razor` component in the following way.

```xml
<div>
    <p><b>Name:</b> @Name</p>
    <p><b>Surname:</b> @Surname</p>
</div>
}
@code {
    [Parameter]
    public string Name { get; set; } = "Denis"

    [Parameter]
    public string Surname { get; set; } = "Ekart"
}
```

The same HTML markup will be generated after the Razor templating engine renders this code. Only now we can provide the rendering engine with additional context (`Name` and `Surname` , in this case), which can render dynamic components based on the information provided. Now, scale this approach to the entire website, and you know [how Razor works](https://learn.microsoft.com/en-us/aspnet/core/mvc/views/razor?view=aspnetcore-8.0) (in a nutshell 🥜).

Rendering web pages is cool and all, but this is not what we're here to do today. We want to (ab)use the Razor rendering engine to render HTML inside a console application.

Why? Well, first of all, because since .NET 8 onwards, we can. But more seriously, there are various use cases where rendering HTML can be used and useful outside of an HTTP request. Generating static website content, structuring rich email messages, and generating PDFs are just a few worthy mentions where templating comes in handy.

Since I've been dealing with a clunky HTML snippet, any time I send out a newsletter for our developer user group, let's convert that into a Razor template (and make my life a little easier).

### Let's start

<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text">You can find all the code related to this article in <a target="_blank" rel="noopener noreferrer nofollow" href="https://github.com/denisekart/dotnet-snacks-samples/tree/main/src/s01-razor-template-rendering" style="pointer-events: none">this repository</a>.</div>
</div>

We are going to create an empty console application that's targeting .net8.

`dotnet new console -n ConsoleRazorRenderer`

If you have [.net8 installed](https://dotnet.microsoft.com/en-us/download/dotnet/8.0), the created `ConsoleRazorRenderer.csproj` will look something like this.

```xml
<Project Sdk="Microsoft.NET.Sdk">
    <PropertyGroup>
        <OutputType>Exe</OutputType>
        <TargetFramework>net8.0</TargetFramework>
        ...
    </PropertyGroup>
</Project>
```

Great. Let's modify our project to allow us to include razor files. We will first need to change the project SDK to import the Razor-specific toolset. Open up the `ConsoleRazorRenderer.csproj` project file and change the `<Project Sdk="Microsoft.NET.Sdk">` to `Microsoft.NET.Sdk.Razor`.

We will also need a couple of external dependencies.

`dotnet add package Microsoft.AspNetCore.Components.Web` will add the services needed to render the Razor components in our console application.

Additionally, we require `dotnet add package Microsoft.Extensions.Logging` since the rendering engine depends on the types defined in this package.

Our project file should now look like this.

```xml
<Project Sdk="Microsoft.NET.Sdk.Razor">
    <PropertyGroup>
        <TargetFramework>net8.0</TargetFramework>
        ...
    </PropertyGroup>
    <ItemGroup>
        <PackageReference Include="Microsoft.AspNetCore.Components.Web" Version="8.0.1"/>
        <PackageReference Include="Microsoft.Extensions.Logging" Version="8.0.0"/>
    </ItemGroup>
</Project>
```

Okay, It's smooth sailing from here. Let's add our `NewsletterComponent.razor` file to the project and fill it up with the newsletter HTML we've been using so far. Feel free to look at the HTML in the repository, but to get a general idea, this is what the final product should look like.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1707125725027/c050f44a-10ac-49cf-ba02-78b6bc896097.png align="center")

> I'm really sorry for my mad drawing skillz.

We can Identify three variables that will change in any future newsletter version. The title, attached graphics, and the actual content of the newsletter. We can make these dynamic by parametrizing our components.

```csharp
@code {
    [Parameter]
    public string? EventTitle { get; set; }
    [Parameter]
    public string? HeadingImageUrl { get; set; }
}
```

The interesting thing here is the actual content of the newsletter. It makes sense that the content of a newsletter will require rich text features (you know, emojis ✨, formatting, and other appealing features). Let's allow the component to accept any arbitrary HTML snippet as the `Content`.

Obviously, the renderer will not allow us to inject any arbitrary string and render it as HTML without escaping it. This is where the [`MarkupString`](https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.components.markupstring?view=aspnetcore-8.0) comes in handy.

```csharp
@code {
    [Parameter]
    public MarkupString? Content { get; set; }
}
```

It allows us to inject raw HTML markup into our components.

<div data-node-type="callout">
<div data-node-type="callout-emoji">⚠</div>
<div data-node-type="callout-text">Injecting unsanitized HTML can make your code insecure and lead to <a target="_blank" rel="noopener noreferrer nofollow" href="https://learn.microsoft.com/en-us/aspnet/core/security/cross-site-scripting?view=aspnetcore-8.0" style="pointer-events: none">XSS attacks</a>. Be mindful when injecting user input into your pages, regardless of where and how they will be used.</div>
</div>

If we translate everything above into Razor, we can generate a component (or multiple components) that will look something like the following.

The *wrapping* component `NewsletterComponent.razor` will be used as a top-level component containing our styling, content, and graphics.

```xml
<!DOCTYPE HTML
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional //EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>SloDug Newsletter | @Content?.Title</title>
    
        <style type="text/css">
            ...
        </style>
    <body>
        <div>
            ...
            <NewsletterContentComponent Content="@Content?.Content" EventTitle="@Content?.Title" HeadingImageUrl="@Content?.HeadingImageUrl" />
        </div>
        <div>
            ...        
            <NewsletterTrailerComponent />
        </div>
    </body>
</html>

@code {
    public record NewsletterDto(string? Title, MarkupString? Content, string? HeadingImageUrl);
    
    [Parameter]
    public NewsletterDto? Content { get; set; }
}
```

`NewsletterContentComponent.razor` and `NewsletterTrailerComponent.razor` will hold dynamic content that will be rendered. While `NewsletterTrailerComponent` will wrap the static footer of our newsletter (not interesting), the `NewsletterContentComponent` will be used to format our dynamic content into HTML.

While there are numerous ways to structure the component hierarchy for any content, that's not the fun part of the article. Let's see how we can render our creation.

We will use the [`HtmlRenderer`](https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.components.web.htmlrenderer?view=aspnetcore-8.0) to output the generated HTML as a string.

Let's first define a rendering service, which will take the `NewsletterDto` we defined earlier and produce a rendered HTML string.

```csharp
public class NewsletterRenderer(HtmlRenderer renderer)
{
    public async Task<string> RenderHtml(NewsletterComponent.NewsletterDto content)
    {
        var result = await renderer.Dispatcher.InvokeAsync(async () =>
        {
            var parameters = ParameterView.FromDictionary(new Dictionary<string, object?>
            {
                { "Content", content }
            });
            var output = await renderer.RenderComponentAsync<NewsletterComponent>(parameters);

            return output.ToHtmlString();
        });

        return result;
    }
}
```

The service will receive a `HtmlRenderer` injected into the constructor ([check out C# 12 primary constructors](https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/tutorials/primary-constructors)). We invoke the renderer using its dispatcher to render the `NewsletterComponent`. All we need now is to supply the renderer with the dynamic content, so we will construct a [`ParameterView`](https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.components.parameterview?view=aspnetcore-8.0) from the content the component expects - the `"Content"` parameter expects an object of type `NewsletterDto`.

The `RenderComponentAsync` method call will return a [`HtmlRootComponent`](https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.components.web.htmlrendering.htmlrootcomponent?view=aspnetcore-8.0) , which contains the rendering result and has a handy `ToHtmlString` method. Now that we have our HTML output, we need to return it to the caller.

It's that simple. Now, let's put it all together. In the `Program.cs` entrypoint, we will create a `ServiceCollection`, add the necessary services, and build our DI container.

```csharp
var provider = new ServiceCollection()
    .AddLogging()
    .AddTransient<HtmlRenderer>()
    .AddTransient<NewsletterRenderer>()
    .BuildServiceProvider();
```

<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text">Alternatively, we could start off with a generic host from the <code>Microsoft.Extensions.Hosting</code> NuGet package and supply the needed services to the host builder.</div>
</div>

Now that we have our service provider, we can resolve the renderer by calling

```csharp
var renderer = provider.GetRequiredService<NewsletterRenderer>();
```

The only thing left is to define our content and render the HTML.

```csharp
var content = new NewsletterComponent.NewsletterDto(
    Title: "Pre NTK - DevOps Edition",
    // language=html
    Content: new("""
                 (...)
                 <div>
                     🗓️ WHEN: Wednesday, January 17th 2024, at 6PM <br />
                     📍 WHERE: BTC City Ljubljana <br />
                     🔗 <a ...>Sign up here</a> <br />
                 </div>
                 (...)
                 """),
    HeadingImageUrl: "https://slodug.blob.core.windows.net/uploads/a9424857-0326-49a4-bbb0-b89ecb9a1038/SloDug_wide_empty.png");

var newsletterHtml = await renderer.RenderHtml(content);
// send the newsletter to subscribers (or something)
```

And that's it. We can now render dynamic content using the Razor rendering engine. This also allows us to reuse existing components we use throughout our web application, making things even easier.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1707166820793/e3e18990-2510-44f3-af44-e8adffa2c65f.png align="center")

<div data-node-type="callout">
<div data-node-type="callout-emoji">👂</div>
<div data-node-type="callout-text">This is not a promo. It's just me trying to solve a fun problem and showcase the solution.</div>
</div>

Newsletter sent!

Don't forget to check out [the repository](https://github.com/denisekart/dotnet-snacks-samples/tree/main/src/s01-razor-template-rendering) for a complete sample.

✌️
