Architecture Overview
Introduction
Rizzy is designed to bridge the gap between traditional server-side ASP.NET Core applications (MVC or Minimal APIs) and modern dynamic user interfaces powered by HTMX. It achieves this by leveraging Blazor’s component model for server-side rendering (SSR) of HTML fragments, which are then delivered to the client for HTMX to handle.
This document provides a high-level overview of Rizzy’s architecture and the flow of requests within a Rizzy-enabled application.
Core Philosophy
The central idea behind Rizzy is to:
- Use Blazor Components for UI: Define UI structures and logic using familiar
.razor
components. - Render Server-Side: Utilize Blazor’s efficient server-side rendering capabilities (
HtmlRenderer
) to generate HTML. - Enhance with HTMX: Use HTMX attributes on the client-side to trigger requests for partial page updates.
- Return HTML Fragments: Have server endpoints return only the necessary HTML fragments generated by Blazor components, rather than full pages or JSON data.
- Leverage HTMX for Swapping: Let HTMX handle swapping the received HTML fragments into the correct places in the DOM.
This approach allows developers to benefit from Blazor’s component model without the complexity of Blazor Server’s SignalR connection or Blazor WebAssembly’s client-side runtime for every interaction, reserving full interactivity for specific components where needed (often augmented by libraries like Alpine.js).
Request Flow
Understanding the request lifecycle is key to understanding Rizzy. There are two primary flows: Initial Page Load and HTMX Partial Update.
Initial Page Load (Standard Request)
- Browser Request: The user navigates to a URL, sending a standard HTTP GET request.
- ASP.NET Core Pipeline: The request goes through routing and standard middleware.
- Controller/Endpoint Execution: The request reaches an MVC Controller action or a Minimal API endpoint.
- Rizzy Service Call: The action/endpoint typically calls
rizzyService.View<TComponent>(...)
. RzPage
Component: Rizzy prepares parameters for theRzPage
component, including the target component (TComponent
) and any data.- Layout Selection:
RzPage
determines the appropriate layout:- It checks for a
@layout
directive onTComponent
. - If none, it uses the
DefaultLayout
configured viaRizzyConfig
. - The layout is likely wrapped in
HtmxApp<TLayout>
andHtmxLayout<TLayout>
. Since this is not an HTMX request,HtmxLayout
renders the full specified layout (TLayout
).
- It checks for a
- Blazor SSR: The Blazor
HtmlRenderer
processes the component tree (RzPage
->HtmxApp
->HtmxLayout
->YourLayout
->TComponent
). - Full HTML Response: The renderer generates the complete HTML for the page.
- Browser Renders: The browser receives and renders the full HTML page. HTMX attributes (
hx-get
,hx-post
, etc.) are now present in the DOM, ready for user interaction.
HTMX Partial Update Request
- User Interaction: The user interacts with an element containing HTMX attributes (e.g., clicks a button with
hx-post
). - Browser Request (HTMX): The browser, via HTMX, sends an AJAX request (GET, POST, etc.) to the specified URL. Crucially, this request includes HTMX-specific headers (like
HX-Request: true
,HX-Target
,HX-Trigger
). - ASP.NET Core Pipeline: The request flows through the pipeline.
- Rizzy Middleware: May intercept the request (e.g., to add nonce headers if configured).
- Antiforgery Middleware: Validates antiforgery tokens if applicable.
- Controller/Endpoint Execution: The request reaches the designated action/endpoint.
[HtmxRequest]
attribute might guard the action.InitializeBlazorFormData
(viaRzController
) might run to enable[SupplyParameterFromForm]
.
- Rizzy Service Call: The action often calls
rizzyService.PartialView<TComponent>(...)
orrizzyService.View<TComponent>(...)
. - Component Rendering (
RzPartial
orRzPage
):- If
PartialView
was called,RzPartial
is typically used. It rendersTComponent
within anEmptyLayout
. - If
View
was called,RzPage
is used.HtmxLayout
detects theHX-Request
header and renders a minimal or empty layout instead of the full application layout.
- If
- Blazor SSR: The
HtmlRenderer
processes the (potentially smaller) component tree. - Response Generation:
- The renderer generates the HTML fragment for the requested component (
TComponent
). - HTMX Response Headers: The controller/endpoint uses
HttpContext.Response.Htmx()
(or[HtmxResponseAttribute]
) to set specific HTMX response headers (e.g.,HX-Trigger
,HX-Retarget
,HX-Reswap
). - OOB Swaps: If the
HtmxSwapService
was used during the request, its content (HtmxSwappable
components) is added to the response, marked for Out-of-Band swapping (hx-swap-oob
).
- The renderer generates the HTML fragment for the requested component (
- Partial HTML Response: The server sends back the HTML fragment and the HTMX response headers.
- Browser (HTMX Handling):
- HTMX receives the response.
- It processes any OOB swap instructions.
- It swaps the main HTML fragment into the DOM element specified by the original
hx-target
(or modified byHX-Retarget
), using the specified swap style (or modified byHX-Reswap
). - It processes other response headers (e.g., triggers client-side events via
HX-Trigger
, redirects viaHX-Redirect
/HX-Location
, updates browser history viaHX-Push-Url
/HX-Replace-Url
).
Key Architectural Pieces
- Controllers / Minimal API Endpoints: Handle incoming HTTP requests, perform business logic, and decide which Blazor component(s) to render.
IRizzyService
: Provides theView<T>
andPartialView<T>
methods, abstracting the component rendering logic for controllers/endpoints.RzPage
/RzPartial
: Core Rizzy components responsible for setting up the rendering environment for Blazor components, including layout selection.
- Blazor SSR (
HtmlRenderer
): The engine that takes the Blazor component tree and renders it to static HTML on the server.HtmxApp<T>
/HtmxLayout<T>
: Manage the application’s root layout and intelligently switch between full and minimal rendering based on HTMX request headers.HtmxRequest
/HtmxResponse
: Classes and extensions providing strongly-typed access to read HTMX request headers and write HTMX response headers.
HtmxSwapService
/HtmxSwappable
: Facilitate server-driven Out-of-Band (OOB) swaps.- Rizzy Form Components (
RzInput*
, etc.): Extend Blazor inputs to integrate withEditContext
and potentially generatedata-val-*
attributes.DataAnnotationsProcessor
/[SupplyParameterFromForm]
: Bridge MVC validation and model binding concepts with Blazor components (Note:[SupplyParameterFromForm]
currently relies on internal reflection).
- Rizzy Middleware: Optional pipeline component for handling cross-cutting concerns like nonce headers.
Benefits of this Architecture
- Developer Experience: Use familiar Blazor component syntax for UI.
- Reduced JavaScript: HTMX handles most dynamic updates declaratively in HTML.
- Leverages SSR: Fast initial loads and SEO benefits from server-rendered HTML.
- Component Reusability: Blazor components can be reused across different parts of the application.
- Integration: Fits naturally within existing ASP.NET Core MVC or Minimal API applications.
Considerations
- State Management: Components rendered via SSR are typically stateless between requests. Managing state across HTMX interactions requires careful consideration.
- Performance: Blazor component rendering adds overhead compared to returning raw HTML strings. The impact depends on component complexity.
- Internal Reflection: Be aware that enabling
[SupplyParameterFromForm]
currently relies on reflection into internal framework APIs (see Forms documentation for details).
This overview provides a conceptual model of how Rizzy works. Refer to specific documentation sections for details on configuration, components, and advanced features.