With all the momentum behind frontend frameworks, it’s often easy to forget that server-side rendering (SSR) is a mature approach to delivering fast and reliable user experiences. Unfortunately, one of the most significant drawbacks to frontend development is the experience can vary wildly depending on factors beyond our control, with the client doing much of the heavy lifting building the experience for the users.
Recently, this has led many frontend frameworks to adopt some variation of SSR to limit the time to the first interaction. As a user, you don’t want to sit around waiting for the experience to begin. The attempt to improve the user experience has added complexity to the build process of many of these frameworks. What if I told you there was a JavaScript library that attempts to reduce build complexity by leaning on the strengths of an SSR framework like ASP.NET Core?
Well, you’re in luck. There’s such a library, and it’s called HTMX. In this post, we’ll look through the library itself and a NuGet package I created to help ASP.NET Core developers get a seamless experience with their technology of choice.
What Is HTMX?
HTMX is a JavaScript library that allows you to build client-side experiences by leveraging SSR to product replacement snippets for existing elements on a page. The way it helps you accomplish building experiences is by decorating HTML with known attributes. The primary focus of HTMX is to allow you to access modern browser features directly from your HTML rather than using copious amounts of JavaScript.
Let’s look at an example directly from the HTMX documentation page.
<button hx-post="/clicked"
hx-trigger="click"
hx-target="#parent-div"
hx-swap="outerHTML">
Click Me!
</button>
In the previous HTMX declaration, when you click the button, HTMX will issue a POST to our clicked
endpoint and use the response, which should be HTML, to replace the hx-target
element of parent-div
in the existing page.
- Now, any element, not just anchors and forms, can issue an HTTP request
- Now, any event, not just clicks or form submissions, can trigger requests
- Now, any HTTP verb, not just GET and POST, can be used
- Now, any element, not just the entire window, can be the target for an update by the request
The most significant difference between the single-page application approach and the HTMX approach is that your server-side technology is no longer just a JSON generator. Instead, your server can perform increasingly complex tasks to return the necessary result.
For folks working in the .NET space, they’ll likely see many similarities between HTMX and XAML-based frameworks: a declarative approach focused on what you want as an outcome, rather than an imperative process explaining each step to get the results you want.
Please take a few minutes to read the official documentation of HTMX; it’s short and descriptive and should get you thinking about all the fantastic possibilities.
HTMX and ASP.NET Core?
While HTMX is primarily a JavaScript library, it sends critical information to the server via request headers. These headers allow an SSR framework, like ASP.NET Core, to react to an HTMX request differently from a standard client request. The essential headers include:
-
HX-Current-URL
: The current URL as seen by the client/browser. -
HX-prompt
: The user response from an HTMX prompt, like a delete confirmation. -
HX-Request
: Will betrue
when HTMX issues the incoming request.
There are also response headers we, as developers, can send back to our frontend to help influence different behavior:
-
HX-Push
: pushes a new URL into the history stack -
HX-Redirect
: force a client-side redirect to a new location. -
HX-Refresh
: force an entire page refresh. -
HX-Trigger
: trigger a client-side event to perform some other client-side behaviors.
All these are easy enough to use with ASP.NET Core, but I’ve taken the liberty of formalizing them into a NuGet Package called HTMX. To get started, you first need to install the package into your ASP.NET Core applications.
dotnet add package Htmx
After installing the package, you can use the HTMX extension methods on the HttpRequest
class to determine if the current request occurred because of an HTMX interaction.
httpContext.Request.IsHtmx()
More commonly, you’ll use it within ASP.NET Core MVC actions or Razor Pages to perform a ternary return.
// in a Razor Page
return Request.IsHtmx()
? Partial("_Form", this)
: Page();
This pattern allows you to support both the regular client interaction and any subsequent HTMX requests. You can also retrieve any header values that HTMX commonly sends using the IsHtmx
overload with an out
parameter.
Request.IsHtmx(out var values);
Using these straightforward extension methods, you can begin to support an HTMX experience on the client-side, but what about support for generating URLs?
HTMX TagHelpers for ASP.NET Core
The heart of any great SSR is always routing, more notably the ability to generate links deterministically. ASP.NET Core is no exception, allowing you to create links to Razor pages, ASP.NET Core MVC actions, and other endpoints. In addition, generating links enables you to refactor URL paths without the need to track down hundreds of static string references.
ASP.NET Core ships with HTML tag helpers that integrate with anchor elements to help you lean on some of the concepts found in the framework.
<a asp-page="Index" />
<a asp-controller="Home" asp-action="Index" />
The previous Razor code may translate into the following HTML output.
<a href="/" />
<a href="/home/" />
ASP.Net Core route helpers are convenient, so I wanted to bring that to the folks using HTMX to enhance their ASP.NET Core application. I have ported the tag helper methods to support HTMX’s hx-get
, hx-put
, hx-post
, and hx-delete
attributes. The first step is to install the NuGet package Htmx.TagHelpers
.
dotnet add package Htmx.TagHelpers
The next step is to modify your Razor pages with the appropriate attributes.
<div hx-target="this">
<button hx-get
hx-page="Index"
hx-page-handler="Snippet"
hx-swap="outerHtml">
Click Me (Razor Page w/ Handler)
</button>
</div>
<div hx-target="this">
<button hx-get
hx-controller="Home"
hx-action="Index"
hx-route-id="1">
Click Me (Controller)
</button>
</div>
<div hx-target="this">
<button hx-post
hx-route="named">
Click Me (Named)
</button>
</div>
Any HTML element has an hx
attribute, you can use similar tag helper attributes to generate an appropriate link. Using the helpers will reduce the number of magic strings found in your Razor files and help reduce breakage during route refactorings.
It’s that easy!
Show Me The Samples!
I’ve written some HTMX samples using ASP.NET Core as a backend for folks interested in pursuing HTMX as a client-side alternative to SPA frameworks with some everyday interactions folks may be trying to accomplish:
- Form Validation
- Updating targets when a user interacts with an element
- Polling updates for server changes
- Editable table rows
- Websocket chat client (experimental)
While the samples are impressive, they only begin to scratch the surface of what’s possible when you combine ASP.NET Core and HTMX. Check it out at my repository.
Conclusion
HTMX is a declarative approach to progressively enhancing your client-side experiences. By leveraging ASP.NET Core SSR frameworks and their strengths, you can reduce the complexity of many SPA frameworks in your projects. Furthermore, using the HTMX NuGet packages I’ve developed, you’ll get a very familiar experience with your current ASP.NET Core development process.
I hope you try it out and let me know your thoughts on Twitter @buhakmeh. Thank you again for reading, and I hope you found this post helpful.