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:
- A
RenderModelthat contains the view model to be exposed to the template (accessible inside the template asview_model). - A
LiquidRoutethat 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– TheIFileProviderthat contains your Liquid templates and any related assets.LiquidTemplatePath– The path to the Liquid template file.
Important: The
LiquidTemplatePathis always relative to the root of the suppliedFileProvider. You do not need to provide aRoutePatternas you would for a normalLiquidPageModel, 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
- Build the
RenderModel– TheViewModelproperty is set to whatever object you want exposed to the template. In this example, aChatMessagesViewModelis created from a list of chat messages. - Resolve the
IFileProvider–_liquidFileProvider.GetFileProvider(typeof(ModuleServices).Assembly)returns the file provider associated with the assembly containing your templates. This is the sameILiquidFileProviderpattern used elsewhere in Liquid Pages. - Construct the
LiquidRoute– Only theFileProviderandLiquidTemplatePathare required. ThetemplatePathhere is relative to the file provider's root (e.g.,"chat/messages.liquid"). - Call
RenderHtml–_htmlRenderer.RenderHtml(renderModel, liquidRoute)returns the fully rendered HTML as a string, ready to be returned, streamed, emailed, or stored as needed.- 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.
- 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
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.
