I love the simplicity of Htmx and the dev experience of Razor Components, so today I’m going to show you how to use both of them together. We’re going to be using the standard .NET 8 webapp
template, Razor Pages and layouts for the main pages, and Razor Components for the Htmx interactivity. This gives us the best of both simplicity worlds, using Razor pages for our overall app navigation and logic, and the templating an in-line code of Razor Components for our UX components.
Let’s start with a basic webapp
templates:
|
|
The first thing we’re going to do is add two lines to the Program.cs
file:
|
|
Routing
Now we have Razor Component support, but you may notice App
isn’t defined. That’s because there isn’t a App
class to direct routes to. For that, we’ll need a new App.razor
file. Because we want our Razor components. In this file is normally a full HTML layout, but because we’re using Razor Components to return partial HTML within our Htmx calls, we’ll remove all that and just leave a Router
component. This will allow us to use the @page
directive to route calls to our components.
|
|
Layout for Razor Pages
Next we need to define the layout for our Razor Pages. Our Razor Pages will be the basis of our application, including all the navigation, so unifying the look-and-feel is important. We’ll start by simplifying the _Layout.cshtml
that comes int the webapp
template. Let’s replace it completely with a simple template:
|
|
Here we’re just referencing the Htmx package and adding the @RenderBody() tag. Next, let’s simplify Index.cshtml
by replacing it with the following:
|
|
As you can see, we’ve got our first hint at some Htmx goodness with our hx-get
, hx-trigger
and hx-swap
attributes.
Our First Component ☀️
Let’s add the current weather to our home page. Index.cshtml
is asking Htmx to swap the contents from an Ajax call to /weather
into that div
. I like to create a new folder for components, so I’ll create Components
as a top-level folder. From there, I’ll create a new file called Weather.razor
:
|
|
Let’s run it! And … uh-oh, exception:
InvalidOperationException: Endpoint /weather (/weather) contains anti-forgery metadata, but a middleware was not found that supports anti-forgery. Configure your application startup by adding app.UseAntiforgery() in the application startup code. If there are calls to app.UseRouting() and app.UseEndpoints(…), the call to app.UseAntiforgery() must go between them. Calls to app.UseAntiforgery() must be placed after calls to app.UseAuthentication() and app.UseAuthorization().
So we have two options, either add the global app.UseAntiforgery();
after app.UseRouting();
, or disable Antiforgery on our Razor components:
|
|
And just like that, we have our weather! You can see below that the browser is making a call to /weather
and just returning the partial rendering from the Razor Component.
Source
The code we generated today is available on my Github: