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: