Kinetq Development Group, LLC. - Rendering Liquid Templates with IHtmlRenderer

While the LiquidMiddleware is the most common way to render Liquid templates in a Liquid Pages application, it's not the only option. There are situations where you may need to render a Liquid template into an HTML string outside of the request/response pipeline — for example, when generating HTML for emails, server-sent events, AI chat responses, background jobs, or any other scenario where you simply need a string of HTML rather than a full HTTP response.

For these cases, Liquid Pages exposes an injectable interface called IHtmlRenderer.

What is IHtmlRenderer?

IHtmlRenderer is a service registered automatically when you call services.AddLiquidPages(). It allows you to render any Liquid template directly to an HTML string by providing:

  1. A RenderModel that contains the view model to be exposed to the template (accessible inside the template as view_model).
  2. A LiquidRoute that tells the renderer where the template lives.

Because it bypasses the middleware entirely, there is no routing, no request model, and no response model involved — just template + data → HTML.

The LiquidRoute Requirements

When using IHtmlRenderer, the LiquidRoute you provide only needs two properties:

  • FileProvider – The IFileProvider that contains your Liquid templates and any related assets.
  • LiquidTemplatePath – The path to the Liquid template file.

Important: The LiquidTemplatePath is always relative to the root of the supplied FileProvider. You do not need to provide a RoutePattern as you would for a normal LiquidPageModel, because no request matching takes place.

Example

The following example demonstrates how to use IHtmlRenderer to render a chat messages view into HTML. The view model is built from a collection of text messages, and the template is loaded from an embedded file provider scoped to the ModuleServices assembly:

var renderModel = new RenderModel
{
    ViewModel = new ChatMessagesViewModel()
    {
        Messages = resultingTextMessages.Select(r => new ChatMessageViewModel
        {
            Role = r.Role.ToString(),
            Content = r.Text
        }).ToList()
    }
};

var fileProvider = _liquidFileProvider.GetFileProvider(typeof(ModuleServices).Assembly);
var liquidRoute = new LiquidRoute()
{
    FileProvider = fileProvider,
    LiquidTemplatePath = templatePath
};

return await _htmlRenderer.RenderHtml(renderModel, liquidRoute);

What's Happening Here

  1. Build the RenderModel – The ViewModel property is set to whatever object you want exposed to the template. In this example, a ChatMessagesViewModel is created from a list of chat messages.
  2. Resolve the IFileProvider_liquidFileProvider.GetFileProvider(typeof(ModuleServices).Assembly) returns the file provider associated with the assembly containing your templates. This is the same ILiquidFileProvider pattern used elsewhere in Liquid Pages.
  3. Construct the LiquidRoute – Only the FileProvider and LiquidTemplatePath are required. The templatePath here is relative to the file provider's root (e.g., "chat/messages.liquid").
  4. Call RenderHtml_htmlRenderer.RenderHtml(renderModel, liquidRoute) returns the fully rendered HTML as a string, ready to be returned, streamed, emailed, or stored as needed.
    1. RenderHtml will utilize all your currently registered types, which are needed if you want to pass POCO objects to the template. If the type isn't already registered then you will have to make sure to register it on startup with the ILiquidRegisteredTypesManager.

When to Use IHtmlRenderer

Use IHtmlRenderer whenever you need rendered Liquid output without going through the HTTP pipeline. Common scenarios include:

  • Generating HTML fragments for AI chat responses or streaming endpoints.
  • Producing HTML email bodies from Liquid templates.
  • Building HTML strings inside background workers or scheduled jobs.
  • Composing partial views inside custom filters or other services.

Do you have more questions? Contact us

Contact us