Working with HTMX and Razor Components

By FoxLearn 2/7/2025 8:18:32 AM   92
In .NET 8, working with Razor components has become an efficient way to handle HTML rendering in a clean and reusable format.

Similar to how JSX is used in React, Razor components combine HTML with C# code to produce dynamic and interactive web pages. With the introduction of the RazorComponentResult type in .NET 8, we now have a powerful tool to enhance our HTMX development.

In our previous guide, Introduction to HTMX with .NET, we worked with the HtmlContentBuilder class to generate HTML dynamically. With the RazorComponentResult class, we can now take full advantage of Razor components while working seamlessly with HTMX.

Let's walk through a practical example where we integrate Razor components to display and manage a product catalog.

Step 1: ProductComponent.razor

First, let's create the ProductComponent.razor file to render a single product in the catalog:

<div>
    <h3>@Model.Name</h3>
    <p>@Model.Description</p>
    <p>@Model.Price</p>
    <button hx-delete="/products/@Model.Id" hx-target="closest div" hx-swap="outerHTML">Remove</button>
</div>

@code {
    [Parameter]
    public ProductModel Model { get; set; } = new();
}

In this file, we render the product's name, description, and price, along with a button to remove the product from the list. The hx-delete attribute will trigger an HTMX call to delete the product.

Step 2: ProductFormComponent.razor

Next, we create a form to allow the user to add a new product to the catalog:

<form hx-post="/products" hx-swap="beforebegin" hx-ext="json-enc">
    <input type="text" name="name" placeholder="Product Name" />
    <input type="text" name="description" placeholder="Product Description" />
    <input type="number" name="price" placeholder="Price" />
    <button type="submit">Add Product</button>
</form>

@code { }

Step 3: ProductListComponent.razor

Now, we will create the ProductListComponent.razor to loop through all products and display them using the ProductComponent:

<div>
    @foreach (var product in Model)
    {
        <ProductComponent Model="@product"></ProductComponent>
    }
    <ProductFormComponent></ProductFormComponent>
</div>

@code {
    [Parameter]
    public List<ProductModel> Model { get; set; } = new();
}

Step 4: DocumentComponent.razor

The root component that ties everything together will be the DocumentComponent.razor file:

<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script src="https://unpkg.com/[email protected]" integrity="sha384-FhXw7b6AlE/jyjlZH5iHa/tTe9EpJ1Y55RjcgPbjeWMskSxZt1v9qkxLJWNJaGni" crossorigin="anonymous"></script>
    <script src="https://unpkg.com/htmx.org/dist/ext/json-enc.js"></script>
</head>
<body hx-get="/products" hx-trigger="load" hx-swap="innerHTML">
</body>
</html>

@code { }

Step 5: Program.cs Configuration

Finally, we configure the routes in Program.cs to make the application functional:

using Microsoft.AspNetCore.Http.HttpResults;
using ProductApi;

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorComponents();
var app = builder.Build();
var db = new List<ProductModel>()
{
    new ProductModel() { Id = Guid.NewGuid(), Name = "Laptop", Description = "High-performance laptop", Price = 1200.00M },
    new ProductModel() { Id = Guid.NewGuid(), Name = "Smartphone", Description = "Latest smartphone", Price = 800.00M },
};

app.MapGet("/", () => new RazorComponentResult<DocumentComponent>());
app.MapGet("/products", () => new RazorComponentResult<ProductListComponent>(new { Model = db }));
app.MapPost("/products", (AddProductCommand command) =>
{
    var product = new ProductModel { Id = Guid.NewGuid(), Name = command.Name, Description = command.Description, Price = command.Price };
    db.Add(product);
    return new RazorComponentResult<ProductComponent>(new { Model = product });
});
app.MapDelete("/products/{id}", (string id) =>
{
    var product = db.First(p => p.Id.ToString() == id);
    db.Remove(product);
});
app.Run();

By utilizing Razor components in .NET 8, we can simplify the structure of our web application and create reusable components that encapsulate both logic and HTML. The combination of Razor components and HTMX gives us the flexibility and power needed to build interactive web applications efficiently.