79768671

Date: 2025-09-18 15:59:23
Score: 0.5
Natty:
Report link

I'm adding an updated answer to this using tools from later versions of .Net (8 and 9). The previous solution from @enet served well, but the new approach is done completely in C# without needing to rely on JS or browser rendering at all. Much more streamlined.

It relies on HtmlRenderer, which takes Razor component (componentType in this example) and a dictionary of all parameters. It also needs access to both your service provider and logger factory.

This is the invoking method I use throughout my apps, usable both as a <T> generic and with Type as a parameter (with some simplifications for this example).

public static async Task<string> RenderTemplate<T>(Dictionary<string, object?> parameters, IServiceProvider serviceProvider) where T : IComponent, IEmailTemplateComponent
{

    return await RenderTemplate(typeof(T), parameters, serviceProvider);
}

public static async Task<string> RenderTemplate(Type componentType, Dictionary<string, object?> parameters, IServiceProvider serviceProvider)
{
    if (!typeof(IComponent).IsAssignableFrom(componentType))
    {
        throw new ArgumentException($"Type {componentType.Name} must implement IComponent.", nameof(componentType));
    }

    ILoggerFactory loggerFactory = serviceProvider.GetRequiredService<ILoggerFactory>();
    await using var htmlRenderer = new HtmlRenderer(serviceProvider, loggerFactory);

    return await htmlRenderer.Dispatcher.InvokeAsync(async () =>
    {
        var parameterView = ParameterView.FromDictionary(parameters);

        var output = await htmlRenderer.RenderComponentAsync(componentType, parameterView);
        string htmlString = output.ToHtmlString();
        return htmlString;
    });
}

Here is an example method call:

string renderedEmail = await ComponentRenderer.RenderTemplate<InspectionEmailTemplate>(
        new Dictionary<string, object?>() { { nameof(InspectionEmailTemplate.Project), project },
                { nameof(InspectionEmailTemplate.Inspection), inspection},
                { nameof(InspectionEmailTemplate.SubmittingUserFullName), args.SubmittingUser},
                { nameof(InspectionEmailTemplate.comment), args.comment} },
        _serviceProvider);

See docs for more details:

https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.components.web.htmlrenderer?view=aspnetcore-9.0

https://learn.microsoft.com/en-us/aspnet/core/blazor/components/render-components-outside-of-aspnetcore?view=aspnetcore-9.0

Reasons:
  • Long answer (-1):
  • Has code block (-0.5):
  • User mentioned (1): @enet
  • Self-answer (0.5):
  • Low reputation (0.5):
Posted by: aterbo