diff --git a/README.md b/README.md
index fe2b3ef..6d4969a 100644
--- a/README.md
+++ b/README.md
@@ -174,7 +174,8 @@ public IActionResult LoadMore() => PartialView("_MoreResults");
```
See [Control HTMX responses](https://rameel.github.io/ramstack.htmxtoolkit/articles/responses.html) for all options,
-including Native AOT support and overloads that avoid closures in hot paths.
+including reflection-free serialization of event details, which also supports Native AOT,
+and overloads that avoid closures in hot paths.
## Tag Helpers
diff --git a/docs/api/index.md b/docs/api/index.md
index 0e90dba..aac07b8 100644
--- a/docs/api/index.md
+++ b/docs/api/index.md
@@ -3,16 +3,16 @@
Find types by the task you want to perform. Each API page lists its members, accepted values, and remarks;
the guides show how those pieces fit into an application.
-| Task | Main types | Guide |
-|---|---|---|
-| Detect HTMX requests and read headers | [HttpRequestExtensions](xref:Ramstack.HtmxToolkit.HttpRequestExtensions), [HtmxRequestHeaders](xref:Ramstack.HtmxToolkit.HtmxRequestHeaders) | [Requests](../articles/requests.md) |
-| Set response headers, navigation, and events | [HttpResponseExtensions](xref:Ramstack.HtmxToolkit.HttpResponseExtensions), [HtmxResponse](xref:Ramstack.HtmxToolkit.HtmxResponse), [HtmxResponseHeaders](xref:Ramstack.HtmxToolkit.HtmxResponseHeaders) | [Responses](../articles/responses.md) |
-| Set swap styles, event timing, or a follow-up request | [HtmxSwap](xref:Ramstack.HtmxToolkit.HtmxSwap), [HtmxTriggerTiming](xref:Ramstack.HtmxToolkit.HtmxTriggerTiming), [HtmxLocationOptions](xref:Ramstack.HtmxToolkit.HtmxLocationOptions) | [Response instructions](../articles/responses.md#choose-a-response-instruction) |
-| Select or configure MVC actions | [HtmxRequestAttribute](xref:Ramstack.HtmxToolkit.HtmxRequestAttribute), [HtmxResponseAttribute](xref:Ramstack.HtmxToolkit.HtmxResponseAttribute) | [MVC action selection](../articles/requests.md#select-mvc-actions-declaratively) |
-| Generate URLs in Razor | [HtmxUrlTagHelper](xref:Ramstack.HtmxToolkit.TagHelpers.HtmxUrlTagHelper) | [Request URLs](../articles/tag-helpers.md#generate-request-urls) |
-| Add values, headers, or request options | [HtmxValsTagHelper](xref:Ramstack.HtmxToolkit.TagHelpers.HtmxValsTagHelper), [HtmxHeaderTagHelper](xref:Ramstack.HtmxToolkit.TagHelpers.HtmxHeaderTagHelper), [HtmxRequestTagHelper](xref:Ramstack.HtmxToolkit.TagHelpers.HtmxRequestTagHelper) | [Tag Helpers](../articles/tag-helpers.md) |
-| Register the toolkit and render configuration | [ServiceCollectionExtensions](xref:Ramstack.HtmxToolkit.Hosting.ServiceCollectionExtensions), [HtmxToolkitOptions](xref:Ramstack.HtmxToolkit.Configuration.HtmxToolkitOptions), [HtmxConfigTagHelper](xref:Ramstack.HtmxToolkit.TagHelpers.HtmxConfigTagHelper) | [Configuration](../articles/configuration.md) |
-| Configure a specific HTMX version | [HtmxV1Config](xref:Ramstack.HtmxToolkit.Configuration.HtmxV1Config), [HtmxV2Config](xref:Ramstack.HtmxToolkit.Configuration.HtmxV2Config), [HtmxV4Config](xref:Ramstack.HtmxToolkit.Configuration.HtmxV4Config) | [Choose a version](../articles/choosing-version.md) |
+| Task | Main types | Guide |
+|-------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------|
+| Detect HTMX requests and read headers | [HttpRequestExtensions](xref:Ramstack.HtmxToolkit.HttpRequestExtensions), [HtmxRequestHeaders](xref:Ramstack.HtmxToolkit.HtmxRequestHeaders) | [Requests](../articles/requests.md) |
+| Set response headers, navigation, and events | [HttpResponseExtensions](xref:Ramstack.HtmxToolkit.HttpResponseExtensions), [HtmxResponse](xref:Ramstack.HtmxToolkit.HtmxResponse), [HtmxResponseHeaders](xref:Ramstack.HtmxToolkit.HtmxResponseHeaders) | [Responses](../articles/responses.md) |
+| Set swap styles, event timing, or a follow-up request | [HtmxSwap](xref:Ramstack.HtmxToolkit.HtmxSwap), [HtmxTriggerTiming](xref:Ramstack.HtmxToolkit.HtmxTriggerTiming), [HtmxLocationOptions](xref:Ramstack.HtmxToolkit.HtmxLocationOptions) | [Response instructions](../articles/responses.md#choose-a-response-instruction) |
+| Select or configure MVC actions | [HtmxRequestAttribute](xref:Ramstack.HtmxToolkit.HtmxRequestAttribute), [HtmxResponseAttribute](xref:Ramstack.HtmxToolkit.HtmxResponseAttribute) | [MVC action selection](../articles/requests.md#select-mvc-actions-declaratively) |
+| Generate URLs in Razor | [HtmxUrlTagHelper](xref:Ramstack.HtmxToolkit.TagHelpers.HtmxUrlTagHelper) | [Request URLs](../articles/tag-helpers.md#generate-request-urls) |
+| Add values, headers, or request options | [HtmxValsTagHelper](xref:Ramstack.HtmxToolkit.TagHelpers.HtmxValsTagHelper), [HtmxHeaderTagHelper](xref:Ramstack.HtmxToolkit.TagHelpers.HtmxHeaderTagHelper), [HtmxRequestTagHelper](xref:Ramstack.HtmxToolkit.TagHelpers.HtmxRequestTagHelper) | [Tag Helpers](../articles/tag-helpers.md) |
+| Register the toolkit and render configuration | [ServiceCollectionExtensions](xref:Ramstack.HtmxToolkit.Hosting.ServiceCollectionExtensions), [HtmxToolkitOptions](xref:Ramstack.HtmxToolkit.Configuration.HtmxToolkitOptions), [HtmxConfigTagHelper](xref:Ramstack.HtmxToolkit.TagHelpers.HtmxConfigTagHelper) | [Configuration](../articles/configuration.md) |
+| Configure a specific HTMX version | [HtmxV1Config](xref:Ramstack.HtmxToolkit.Configuration.HtmxV1Config), [HtmxV2Config](xref:Ramstack.HtmxToolkit.Configuration.HtmxV2Config), [HtmxV4Config](xref:Ramstack.HtmxToolkit.Configuration.HtmxV4Config) | [Choose a version](../articles/choosing-version.md) |
New to the library? Start with [Getting started](../articles/getting-started.md) for a working Razor Pages
application, or choose the [MVC](../articles/mvc.md) or [Minimal API](../articles/minimal-apis.md) example.
diff --git a/docs/articles/antiforgery.md b/docs/articles/antiforgery.md
index 6f7fe28..26a150d 100644
--- a/docs/articles/antiforgery.md
+++ b/docs/articles/antiforgery.md
@@ -9,12 +9,12 @@ The Toolkit script, `htmx-toolkit.min.js`, comes with the package as a static we
It is not the HTMX library itself. Load it after HTMX on every page that uses HTMX;
see [Static assets and deployment](static-assets.md) for the URL and caching.
-| Feature | HTMX versions | Without the script |
-|---|---|---|
-| Adds the antiforgery token to non-GET HTMX requests and refreshes it after boosted navigation | All | Non-GET requests without a token field fail server-side antiforgery validation, usually with HTTP 400 |
-| Sends `HX-Request-Type`; see [Check full or partial requests](requests.md#check-full-or-partial-requests) | 1.9.x and 2.x; HTMX 4.x sends it itself | `IsHtmxPartialRequest()` and `IsHtmxFullRequest()` return `false`, and `[HtmxRequest]` with a `RequestType` does not match |
-| Replays events whose name repeats in one response header; see [Timing and repeated events](responses.md#timing-and-repeated-events) | All | None of the events in that header fire |
-| Adds the `ramstack-swap` extension with `innerMorph`, `outerMorph`, and a simplified `outerSync`, plus `textContent` for HTMX 1.9.x; see [Morph swaps](version-compatibility.md#morph-swaps) | 1.9.x and 2.x; HTMX 4.x supports these swap styles natively | These swap styles are not available |
+| Feature | HTMX versions | Without the script |
+|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------|
+| Adds the antiforgery token to non-GET HTMX requests and refreshes it after boosted navigation | All | Non-GET requests without a token field fail server-side antiforgery validation, usually with HTTP 400 |
+| Sends `HX-Request-Type`; see [Check full or partial requests](requests.md#check-full-or-partial-requests) | 1.9.x and 2.x; HTMX 4.x sends it itself | `IsHtmxPartialRequest()` and `IsHtmxFullRequest()` return `false`, and `[HtmxRequest]` with a `RequestType` does not match |
+| Replays events whose name repeats in one response header; see [Timing and repeated events](responses.md#timing-and-repeated-events) | All | None of the events in that header fire |
+| Adds the `ramstack-swap` extension with `innerMorph`, `outerMorph`, and a simplified `outerSync`, plus `textContent` for HTMX 1.9.x; see [Morph swaps](version-compatibility.md#morph-swaps) | 1.9.x and 2.x; HTMX 4.x supports these swap styles natively | These swap styles are not available |
## How automatic antiforgery works
diff --git a/docs/articles/choosing-version.md b/docs/articles/choosing-version.md
index cb25851..00086be 100644
--- a/docs/articles/choosing-version.md
+++ b/docs/articles/choosing-version.md
@@ -36,14 +36,14 @@ trying to select a different version causes startup validation to fail.
## What the selection changes
-| Behavior | HTMX 1.9.x | HTMX 2.x | HTMX 4.x |
-|---|---|---|---|
-| Default when omitted | No | Yes | No |
-| Global config type | `HtmxV1Config` | `HtmxV2Config` | `HtmxV4Config` |
-| Per-request attribute | `hx-request` | `hx-request` | `hx-config` |
-| Triggering element header | `HX-Trigger` | `HX-Trigger` | `HX-Source` |
-| Expected response kind | `HX-Request-Type` from the Toolkit script | `HX-Request-Type` from the Toolkit script | `HX-Request-Type` |
-| Morph swap support | Toolkit compatibility extension | Toolkit compatibility extension | Native |
+| Behavior | HTMX 1.9.x | HTMX 2.x | HTMX 4.x |
+|---------------------------|-------------------------------------------|-------------------------------------------|-------------------|
+| Default when omitted | No | Yes | No |
+| Global config type | `HtmxV1Config` | `HtmxV2Config` | `HtmxV4Config` |
+| Per-request attribute | `hx-request` | `hx-request` | `hx-config` |
+| Triggering element header | `HX-Trigger` | `HX-Trigger` | `HX-Source` |
+| Expected response kind | `HX-Request-Type` from the Toolkit script | `HX-Request-Type` from the Toolkit script | `HX-Request-Type` |
+| Morph swap support | Toolkit compatibility extension | Toolkit compatibility extension | Native |
See [Version compatibility](version-compatibility.md) for the differences that affect application code.
diff --git a/docs/articles/configuration-v1.md b/docs/articles/configuration-v1.md
index e777e0c..2bb2272 100644
--- a/docs/articles/configuration-v1.md
+++ b/docs/articles/configuration-v1.md
@@ -23,16 +23,16 @@ Only non-null settings are written to ``.
## Settings by purpose
-| Purpose | Properties |
-|---|---|
-| History | `HistoryEnabled`, `HistoryCacheSize`, `RefreshOnHistoryMiss` |
-| Swaps | `DefaultSwapStyle`, `DefaultSwapDelay`, `DefaultSettleDelay`, `AttributesToSettle` |
+| Purpose | Properties |
+|------------------------------|------------------------------------------------------------------------------------------------------------|
+| History | `HistoryEnabled`, `HistoryCacheSize`, `RefreshOnHistoryMiss` |
+| Swaps | `DefaultSwapStyle`, `DefaultSwapDelay`, `DefaultSettleDelay`, `AttributesToSettle` |
| Indicators and lifecycle CSS | `IncludeIndicatorStyles`, `IndicatorClass`, `RequestClass`, `AddedClass`, `SwappingClass`, `SettlingClass` |
-| Script and content security | `AllowEval`, `AllowScriptTags`, `InlineScriptNonce`, `DisableSelector` |
-| Requests | `WithCredentials`, `Timeout`, `SelfRequestsOnly`, `MethodsThatUseUrlParams`, `GetCacheBusterParam` |
-| Navigation and focus | `ScrollBehavior`, `DefaultFocusScroll`, `ScrollIntoViewOnBoost`, `IgnoreTitle` |
-| Transitions and parsing | `GlobalViewTransitions`, `UseTemplateFragments`, `TriggerSpecsCacheEnabled` |
-| WebSocket | `WsBinaryType` |
+| Script and content security | `AllowEval`, `AllowScriptTags`, `InlineScriptNonce`, `DisableSelector` |
+| Requests | `WithCredentials`, `Timeout`, `SelfRequestsOnly`, `MethodsThatUseUrlParams`, `GetCacheBusterParam` |
+| Navigation and focus | `ScrollBehavior`, `DefaultFocusScroll`, `ScrollIntoViewOnBoost`, `IgnoreTitle` |
+| Transitions and parsing | `GlobalViewTransitions`, `UseTemplateFragments`, `TriggerSpecsCacheEnabled` |
+| WebSocket | `WsBinaryType` |
`wsReconnectDelay` is not exposed: in JSON, HTMX accepts only its default value `"full-jitter"`.
To customize the WebSocket reconnection delay, assign a function to `htmx.config.wsReconnectDelay` in JavaScript.
diff --git a/docs/articles/configuration-v2.md b/docs/articles/configuration-v2.md
index 097ffc0..87bf030 100644
--- a/docs/articles/configuration-v2.md
+++ b/docs/articles/configuration-v2.md
@@ -21,17 +21,17 @@ builder.Services.AddHtmxToolkit(options =>
## Settings by purpose
-| Purpose | Properties |
-|---|---|
-| History | `HistoryEnabled`, `HistoryCacheSize`, `RefreshOnHistoryMiss`, `HistoryRestoreAsHxRequest` |
-| Swaps | `DefaultSwapStyle`, `DefaultSwapDelay`, `DefaultSettleDelay`, `AttributesToSettle`, `AllowNestedOobSwaps` |
-| Indicators and lifecycle CSS | `IncludeIndicatorStyles`, `IndicatorClass`, `RequestClass`, `AddedClass`, `SwappingClass`, `SettlingClass` |
-| Script and content security | `AllowEval`, `AllowScriptTags`, `InlineScriptNonce`, `InlineStyleNonce`, `DisableSelector` |
-| Requests | `WithCredentials`, `DisableInheritance`, `Timeout`, `SelfRequestsOnly`, `MethodsThatUseUrlParams`, `GetCacheBusterParam` |
-| Navigation and focus | `ScrollBehavior`, `DefaultFocusScroll`, `ScrollIntoViewOnBoost`, `IgnoreTitle` |
-| Transitions and forms | `GlobalViewTransitions`, `TriggerSpecsCacheEnabled`, `ReportValidityOfForms` |
-| Responses | `ResponseHandling` |
-| WebSocket | `WsBinaryType` |
+| Purpose | Properties |
+|------------------------------|--------------------------------------------------------------------------------------------------------------------------|
+| History | `HistoryEnabled`, `HistoryCacheSize`, `RefreshOnHistoryMiss`, `HistoryRestoreAsHxRequest` |
+| Swaps | `DefaultSwapStyle`, `DefaultSwapDelay`, `DefaultSettleDelay`, `AttributesToSettle`, `AllowNestedOobSwaps` |
+| Indicators and lifecycle CSS | `IncludeIndicatorStyles`, `IndicatorClass`, `RequestClass`, `AddedClass`, `SwappingClass`, `SettlingClass` |
+| Script and content security | `AllowEval`, `AllowScriptTags`, `InlineScriptNonce`, `InlineStyleNonce`, `DisableSelector` |
+| Requests | `WithCredentials`, `DisableInheritance`, `Timeout`, `SelfRequestsOnly`, `MethodsThatUseUrlParams`, `GetCacheBusterParam` |
+| Navigation and focus | `ScrollBehavior`, `DefaultFocusScroll`, `ScrollIntoViewOnBoost`, `IgnoreTitle` |
+| Transitions and forms | `GlobalViewTransitions`, `TriggerSpecsCacheEnabled`, `ReportValidityOfForms` |
+| Responses | `ResponseHandling` |
+| WebSocket | `WsBinaryType` |
`wsReconnectDelay` is not exposed: in JSON, the WebSocket extension accepts only its default value `"full-jitter"`.
To customize the reconnection delay, assign a function to `htmx.config.wsReconnectDelay` in JavaScript.
diff --git a/docs/articles/configuration-v4.md b/docs/articles/configuration-v4.md
index bbec919..5c2202e 100644
--- a/docs/articles/configuration-v4.md
+++ b/docs/articles/configuration-v4.md
@@ -22,26 +22,26 @@ builder.Services.AddHtmxToolkit(options =>
## Settings by purpose
-| Purpose | Properties |
-|---|---|
-| Diagnostics and attribute prefix | `LogAll`, `Prefix` |
-| History | `History` |
-| Swaps and morphing | `DefaultSwap`, `AllowEmptySwapAfterOob`, `DefaultSettleDelay`, `MorphIgnore`, `MorphSkip`, `MorphSkipChildren`, `MorphScanLimit` |
-| Indicators and lifecycle CSS | `IncludeIndicatorCss`, `IndicatorClass`, `RequestClass` |
-| Script and extensions | `InlineScriptNonce`, `Extensions`, `ImplicitInheritance` |
-| Requests | `DefaultTimeout`, `Mode` |
-| Focus and transitions | `DefaultFocusScroll`, `Transitions` |
-| Responses | `NoSwap` |
+| Purpose | Properties |
+|----------------------------------|----------------------------------------------------------------------------------------------------------------------------------|
+| Diagnostics and attribute prefix | `LogAll`, `Prefix` |
+| History | `History` |
+| Swaps and morphing | `DefaultSwap`, `AllowEmptySwapAfterOob`, `DefaultSettleDelay`, `MorphIgnore`, `MorphSkip`, `MorphSkipChildren`, `MorphScanLimit` |
+| Indicators and lifecycle CSS | `IncludeIndicatorCss`, `IndicatorClass`, `RequestClass` |
+| Script and extensions | `InlineScriptNonce`, `Extensions`, `ImplicitInheritance` |
+| Requests | `DefaultTimeout`, `Mode` |
+| Focus and transitions | `DefaultFocusScroll`, `Transitions` |
+| Responses | `NoSwap` |
## Request mode
`Mode` maps to the Fetch API request mode:
-| Value | Meaning |
-|---|---|
-| `SameOrigin` | Allow only same-origin requests |
-| `Cors` | Allow cross-origin requests through CORS |
-| `NoCors` | Allow restricted cross-origin requests that produce opaque responses |
+| Value | Meaning |
+|--------------|----------------------------------------------------------------------|
+| `SameOrigin` | Allow only same-origin requests |
+| `Cors` | Allow cross-origin requests through CORS |
+| `NoCors` | Allow restricted cross-origin requests that produce opaque responses |
In `NoCors` mode, the browser allows only `GET`, `HEAD`, and `POST`; other methods raise `htmx:error`.
It also removes request headers that are not CORS-safelisted, even for same-origin requests: the server receives
diff --git a/docs/articles/loading-and-errors.md b/docs/articles/loading-and-errors.md
index b4e3e43..1ba079b 100644
--- a/docs/articles/loading-and-errors.md
+++ b/docs/articles/loading-and-errors.md
@@ -122,11 +122,11 @@ The controller does not need extra response headers for this browser feedback.
The JavaScript above uses HTMX 2.x lifecycle events. HTMX 4 changes their names and event data:
-| Purpose | HTMX 2.x | HTMX 4.x |
-|---|---|---|
-| Clear the previous error before sending | `htmx:beforeRequest` | `htmx:before:request` |
-| Report an HTTP error | `htmx:responseError` | `htmx:response:error` |
-| Report a connection failure or timeout | `htmx:sendError`, `htmx:timeout` | `htmx:error` |
+| Purpose | HTMX 2.x | HTMX 4.x |
+|-----------------------------------------|----------------------------------|-----------------------|
+| Clear the previous error before sending | `htmx:beforeRequest` | `htmx:before:request` |
+| Report an HTTP error | `htmx:responseError` | `htmx:response:error` |
+| Report a connection failure or timeout | `htmx:sendError`, `htmx:timeout` | `htmx:error` |
When porting the listeners, read the request context from `event.detail.ctx`; response status is available
as `ctx.response.status` when a response exists. `htmx:error` also covers exceptions during a swap, so inspect
diff --git a/docs/articles/overview.md b/docs/articles/overview.md
index 41ff724..acda13b 100644
--- a/docs/articles/overview.md
+++ b/docs/articles/overview.md
@@ -11,12 +11,12 @@ The server renders the new HTML; you do not need to build that HTML from JSON in
Four attributes describe most interactions:
-| Attribute | What it controls | Example |
-|---|---|---|
+| Attribute | What it controls | Example |
+|-----------------------|----------------------------|--------------------------------------------|
| `hx-get` or `hx-post` | The request method and URL | `hx-get="/Products?handler=Details&id=42"` |
-| `hx-trigger` | When to send the request | `hx-trigger="click"` |
-| `hx-target` | The element to update | `hx-target="#product-details"` |
-| `hx-swap` | How to insert the response | `hx-swap="innerHTML"` |
+| `hx-trigger` | When to send the request | `hx-trigger="click"` |
+| `hx-target` | The element to update | `hx-target="#product-details"` |
+| `hx-swap` | How to insert the response | `hx-swap="innerHTML"` |
A button uses `click` by default, and a form uses `submit`. The default swap is `innerHTML`, which replaces
the target's contents and keeps the target itself. `outerHTML` replaces the target element too, so the response
@@ -27,15 +27,15 @@ and response headers that tell HTMX what to do next.
## What the toolkit provides
-| Area | Toolkit feature |
-|---|---|
-| Incoming requests | `IsHtmxPartialRequest`, `IsHtmxFullRequest`, `IsHtmxRequest`, `IsHtmxBoosted`, and strongly typed request headers |
-| Outgoing responses | Fluent `Response.Htmx(...)` API and strongly typed response headers |
-| MVC | `[HtmxRequest]` action selection by request type and boosted navigation, and `[HtmxResponse]` response configuration |
-| Razor | Tag Helpers for routes, values, headers, and per-request options |
-| Configuration | Version-specific HTMX configuration rendered by `` |
-| Security | Automatic ASP.NET Core antiforgery headers for unsafe HTMX requests |
-| Browser script | The [Toolkit script](antiforgery.md#what-the-toolkit-script-does), served as a static web asset and loaded from the layout |
+| Area | Toolkit feature |
+|--------------------|----------------------------------------------------------------------------------------------------------------------------|
+| Incoming requests | `IsHtmxPartialRequest`, `IsHtmxFullRequest`, `IsHtmxRequest`, `IsHtmxBoosted`, and strongly typed request headers |
+| Outgoing responses | Fluent `Response.Htmx(...)` API and strongly typed response headers |
+| MVC | `[HtmxRequest]` action selection by request type and boosted navigation, and `[HtmxResponse]` response configuration |
+| Razor | Tag Helpers for routes, values, headers, and per-request options |
+| Configuration | Version-specific HTMX configuration rendered by `` |
+| Security | Automatic ASP.NET Core antiforgery headers for unsafe HTMX requests |
+| Browser script | The [Toolkit script](antiforgery.md#what-the-toolkit-script-does), served as a static web asset and loaded from the layout |
HtmxToolkit does not include the HTMX library and does not replace HTMX attributes such as `hx-target`, `hx-trigger`,
or `hx-swap`. Add a supported HTMX release to the application separately.
@@ -90,14 +90,14 @@ registration, and layout needed to run it.
## Which pieces do I need?
-| Application feature | Setup |
-|---|---|
-| Read request headers or set ordinary response headers | Reference the package and import `Ramstack.HtmxToolkit` in C# |
-| Choose a target version or configure HTMX | Call `AddHtmxToolkit()`; render `` in Razor for browser configuration |
-| Generate URLs or JSON attributes in Razor | Register the toolkit Tag Helpers in `_ViewImports.cshtml` |
-| Attach antiforgery tokens automatically | Render configuration metadata, load the Toolkit script, and enable server validation |
-| Use compatibility morph swaps or replay duplicate event names | Load the Toolkit script; follow the relevant feature's setup |
-| Tell full-page and fragment requests apart with HTMX 1.9.x or 2.x | Load the Toolkit script |
+| Application feature | Setup |
+|-------------------------------------------------------------------|--------------------------------------------------------------------------------------|
+| Read request headers or set ordinary response headers | Reference the package and import `Ramstack.HtmxToolkit` in C# |
+| Choose a target version or configure HTMX | Call `AddHtmxToolkit()`; render `` in Razor for browser configuration |
+| Generate URLs or JSON attributes in Razor | Register the toolkit Tag Helpers in `_ViewImports.cshtml` |
+| Attach antiforgery tokens automatically | Render configuration metadata, load the Toolkit script, and enable server validation |
+| Use compatibility morph swaps or replay duplicate event names | Load the Toolkit script; follow the relevant feature's setup |
+| Tell full-page and fragment requests apart with HTMX 1.9.x or 2.x | Load the Toolkit script |
For HTMX 4.x, register the target version even if you only use response events, so the toolkit chooses the
correct trigger headers. In a pure Minimal API application, HTML rendering and token metadata are supplied
diff --git a/docs/articles/pages-and-fragments.md b/docs/articles/pages-and-fragments.md
index 133d367..0eb1403 100644
--- a/docs/articles/pages-and-fragments.md
+++ b/docs/articles/pages-and-fragments.md
@@ -155,8 +155,11 @@ On ASP.NET Core 7 or later, a policy for public, cacheable HTML could include:
```csharp
builder.Services.AddOutputCache(options =>
{
- options.AddPolicy("HtmxHtml", policy => policy.SetVaryByHeader(
- "HX-Request", "HX-History-Restore-Request", "HX-Request-Type"));
+ options.AddPolicy("HtmxHtml", policy =>
+ policy.SetVaryByHeader(
+ "HX-Request",
+ "HX-History-Restore-Request",
+ "HX-Request-Type"));
});
```
diff --git a/docs/articles/recipes.md b/docs/articles/recipes.md
index a4ebe72..e2b5fc6 100644
--- a/docs/articles/recipes.md
+++ b/docs/articles/recipes.md
@@ -110,7 +110,7 @@ document.addEventListener("profile-saved", event => {
The detail property becomes `displayName` in JSON. Listening on `document` survives replacement of the form.
The HTMX listener above only needs the event name;
it reloads the saved data from the server. Keep it outside the content replaced by the save response.
-See [Responses](responses.md#trigger-client-events) for event timings and Native AOT serialization.
+See [Responses](responses.md#trigger-client-events) for event timings and source-generated serialization.
## Poll a background operation
diff --git a/docs/articles/requests.md b/docs/articles/requests.md
index 42e009d..ca580e3 100644
--- a/docs/articles/requests.md
+++ b/docs/articles/requests.md
@@ -43,17 +43,17 @@ var htmx = Request.GetHtmxHeaders();
## Request header properties
-| Property | Meaning |
-|---|---|
-| `Request` | Whether the `HX-Request` header is present |
-| `Boosted` | Whether a regular navigation was enhanced by `hx-boost` |
-| `CurrentUrl` | Current browser URL when the request was sent |
-| `HistoryRestoreRequest` | Whether HTMX marked the request for history restoration |
-| `Target` | Target element; its format differs in HTMX 4 |
-| `Prompt` | Result of `hx-prompt`; in HTMX 4.x, sent only by the `hx-prompt` extension |
-| `Trigger`, `TriggerName` | Triggering element ID and name in HTMX 1.9.x and 2.x |
-| `Source` | Triggering `tag#id` in HTMX 4.x |
-| `RequestType` | `HtmxRequestType.Partial` or `HtmxRequestType.Full`; added by the Toolkit script in HTMX 1.9.x and 2.x |
+| Property | Meaning |
+|--------------------------|--------------------------------------------------------------------------------------------------------|
+| `Request` | Whether the `HX-Request` header is present |
+| `Boosted` | Whether a regular navigation was enhanced by `hx-boost` |
+| `CurrentUrl` | Current browser URL when the request was sent |
+| `HistoryRestoreRequest` | Whether HTMX marked the request for history restoration |
+| `Target` | Target element; its format differs in HTMX 4 |
+| `Prompt` | Result of `hx-prompt`; in HTMX 4.x, sent only by the `hx-prompt` extension |
+| `Trigger`, `TriggerName` | Triggering element ID and name in HTMX 1.9.x and 2.x |
+| `Source` | Triggering `tag#id` in HTMX 4.x |
+| `RequestType` | `HtmxRequestType.Partial` or `HtmxRequestType.Full`; added by the Toolkit script in HTMX 1.9.x and 2.x |
Missing or version-inapplicable string headers return `null`.
`Request` is `true` whenever the `HX-Request` header is present, whatever its value;
@@ -159,8 +159,8 @@ Without `RequestType`, the attribute accepts any HTMX request. That suits an end
such as a status widget:
```csharp
-[HttpGet("/inventory/{sku}")]
[HtmxRequest]
+[HttpGet("/inventory/{sku}")]
public IActionResult InventoryFragment(string sku) =>
PartialView("_Inventory", inventory.Find(sku));
```
diff --git a/docs/articles/responses.md b/docs/articles/responses.md
index c233120..b4c3130 100644
--- a/docs/articles/responses.md
+++ b/docs/articles/responses.md
@@ -53,18 +53,18 @@ app.MapPost("/account/sign-in", (HttpResponse response, LinkGenerator links) =>
## Choose a response instruction
-| Goal | API | Header |
-|---|---|---|
-| Request another URL through HTMX | `Location` | `HX-Location` |
-| Navigate with a full page load | `Redirect` | `HX-Redirect` |
-| Reload the current page | `Refresh` | `HX-Refresh` |
-| Add a browser history entry | `PushUrl` | `HX-Push-Url` |
-| Replace the current history entry | `ReplaceUrl` | `HX-Replace-Url` |
+| Goal | API | Header |
+|-------------------------------------|------------------------|----------------------|
+| Request another URL through HTMX | `Location` | `HX-Location` |
+| Navigate with a full page load | `Redirect` | `HX-Redirect` |
+| Reload the current page | `Refresh` | `HX-Refresh` |
+| Add a browser history entry | `PushUrl` | `HX-Push-Url` |
+| Replace the current history entry | `ReplaceUrl` | `HX-Replace-Url` |
| Leave the browser history unchanged | `PreventHistoryUpdate` | `HX-Push-Url: false` |
-| Change the swap target | `Retarget` | `HX-Retarget` |
-| Change the swap strategy | `Reswap` | `HX-Reswap` |
-| Select part of the response | `Reselect` | `HX-Reselect` |
-| Dispatch client events | `TriggerEvent` | `HX-Trigger*` |
+| Change the swap target | `Retarget` | `HX-Retarget` |
+| Change the swap strategy | `Reswap` | `HX-Reswap` |
+| Select part of the response | `Reselect` | `HX-Reselect` |
+| Dispatch client events | `TriggerEvent` | `HX-Trigger*` |
> [!TIP]
> `Location`, `Redirect`, `PushUrl`, and `ReplaceUrl` take a URL string. Generate it with `Url.Action`, `Url.Page`,
@@ -217,10 +217,15 @@ Keep the script loaded when using repeated event names: without it, none of the
in that header are triggered. In HTMX 4.x all timings share `HX-Trigger`, so this also applies to a name
added with different timings. Do not use the name `rs:event`.
-### Serialize details with Native AOT
+### Serialize details with source-generated metadata
-The object overload uses reflection to serialize details. For trimming and Native AOT,
-pass source-generated JSON metadata instead. Define the payload and JSON context in their own C# file:
+The object overload discovers the shape of each detail type through reflection the first time it serializes that type.
+Pass source-generated JSON metadata to do this work at build time instead: the first event of each type is serialized
+without the reflection delay, the payload can be written by generated code, and the app needs less memory.
+Because no reflection is involved, this overload also supports trimming and Native AOT.
+
+Each payload needs a named type listed in the JSON context, so anonymous objects cannot be used.
+Define the payload and the context in their own C# file:
```csharp
using System.Text.Json.Serialization;
diff --git a/docs/articles/samples.md b/docs/articles/samples.md
index 838eb78..2681345 100644
--- a/docs/articles/samples.md
+++ b/docs/articles/samples.md
@@ -26,18 +26,18 @@ version-specific options such as `NoSwap`.
Each Razor Page has a `.cshtml` file for the browser markup and a `.cshtml.cs` file for its handlers.
Start with the linked page, then open its code-behind file in the same directory.
-| Task | Demo page | Related guide |
-|---|---|---|
-| Generate a page-handler URL | [UrlTagHelper](https://github.com/rameel/ramstack.htmxtoolkit/blob/main/samples/Ramstack.HtmxToolkit.Demo/Pages/Examples/UrlTagHelper.cshtml) | [Tag Helpers](tag-helpers.md#generate-request-urls) |
-| Send route values | [RouteData](https://github.com/rameel/ramstack.htmxtoolkit/blob/main/samples/Ramstack.HtmxToolkit.Demo/Pages/Examples/RouteData.cshtml) | [Routes and dictionaries](tag-helpers.md#named-routes-and-route-dictionaries) |
-| Send values and custom headers | [HtmxVals](https://github.com/rameel/ramstack.htmxtoolkit/blob/main/samples/Ramstack.HtmxToolkit.Demo/Pages/Examples/HtmxVals.cshtml), [Headers](https://github.com/rameel/ramstack.htmxtoolkit/blob/main/samples/Ramstack.HtmxToolkit.Demo/Pages/Examples/Headers.cshtml) | [Tag Helpers](tag-helpers.md#send-additional-values) |
-| Set request options | [RequestOptions](https://github.com/rameel/ramstack.htmxtoolkit/blob/main/samples/Ramstack.HtmxToolkit.Demo/Pages/Examples/RequestOptions.cshtml) | [Configure one request](tag-helpers.md#configure-one-request) |
-| Detect HTMX and boosted requests | [HtmxRequest](https://github.com/rameel/ramstack.htmxtoolkit/blob/main/samples/Ramstack.HtmxToolkit.Demo/Pages/Examples/HtmxRequest.cshtml), [Boosted](https://github.com/rameel/ramstack.htmxtoolkit/blob/main/samples/Ramstack.HtmxToolkit.Demo/Pages/Examples/Boosted.cshtml) | [Requests](requests.md) |
-| Use MVC attributes | [InventoryController](https://github.com/rameel/ramstack.htmxtoolkit/blob/main/samples/Ramstack.HtmxToolkit.Demo/Controllers/InventoryController.cs) | [MVC action selection](requests.md#select-mvc-actions-declaratively) |
-| Change the response target | [FluentResponse](https://github.com/rameel/ramstack.htmxtoolkit/blob/main/samples/Ramstack.HtmxToolkit.Demo/Pages/Examples/FluentResponse.cshtml) | [Responses](responses.md) |
-| Trigger another update | [ServerEvents](https://github.com/rameel/ramstack.htmxtoolkit/blob/main/samples/Ramstack.HtmxToolkit.Demo/Pages/Examples/ServerEvents.cshtml) | [Forms and events tutorial](forms-and-validation.md) |
-| Poll until work completes | [Polling](https://github.com/rameel/ramstack.htmxtoolkit/blob/main/samples/Ramstack.HtmxToolkit.Demo/Pages/Examples/Polling.cshtml) | [Polling recipe](recipes.md#poll-a-background-operation) |
-| Submit a protected form | [Antiforgery](https://github.com/rameel/ramstack.htmxtoolkit/blob/main/samples/Ramstack.HtmxToolkit.Demo/Pages/Examples/Antiforgery.cshtml) | [Antiforgery](antiforgery.md) |
+| Task | Demo page | Related guide |
+|----------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------|
+| Generate a page-handler URL | [UrlTagHelper](https://github.com/rameel/ramstack.htmxtoolkit/blob/main/samples/Ramstack.HtmxToolkit.Demo/Pages/Examples/UrlTagHelper.cshtml) | [Tag Helpers](tag-helpers.md#generate-request-urls) |
+| Send route values | [RouteData](https://github.com/rameel/ramstack.htmxtoolkit/blob/main/samples/Ramstack.HtmxToolkit.Demo/Pages/Examples/RouteData.cshtml) | [Routes and dictionaries](tag-helpers.md#named-routes-and-route-dictionaries) |
+| Send values and custom headers | [HtmxVals](https://github.com/rameel/ramstack.htmxtoolkit/blob/main/samples/Ramstack.HtmxToolkit.Demo/Pages/Examples/HtmxVals.cshtml), [Headers](https://github.com/rameel/ramstack.htmxtoolkit/blob/main/samples/Ramstack.HtmxToolkit.Demo/Pages/Examples/Headers.cshtml) | [Tag Helpers](tag-helpers.md#send-additional-values) |
+| Set request options | [RequestOptions](https://github.com/rameel/ramstack.htmxtoolkit/blob/main/samples/Ramstack.HtmxToolkit.Demo/Pages/Examples/RequestOptions.cshtml) | [Configure one request](tag-helpers.md#configure-one-request) |
+| Detect HTMX and boosted requests | [HtmxRequest](https://github.com/rameel/ramstack.htmxtoolkit/blob/main/samples/Ramstack.HtmxToolkit.Demo/Pages/Examples/HtmxRequest.cshtml), [Boosted](https://github.com/rameel/ramstack.htmxtoolkit/blob/main/samples/Ramstack.HtmxToolkit.Demo/Pages/Examples/Boosted.cshtml) | [Requests](requests.md) |
+| Use MVC attributes | [InventoryController](https://github.com/rameel/ramstack.htmxtoolkit/blob/main/samples/Ramstack.HtmxToolkit.Demo/Controllers/InventoryController.cs) | [MVC action selection](requests.md#select-mvc-actions-declaratively) |
+| Change the response target | [FluentResponse](https://github.com/rameel/ramstack.htmxtoolkit/blob/main/samples/Ramstack.HtmxToolkit.Demo/Pages/Examples/FluentResponse.cshtml) | [Responses](responses.md) |
+| Trigger another update | [ServerEvents](https://github.com/rameel/ramstack.htmxtoolkit/blob/main/samples/Ramstack.HtmxToolkit.Demo/Pages/Examples/ServerEvents.cshtml) | [Forms and events tutorial](forms-and-validation.md) |
+| Poll until work completes | [Polling](https://github.com/rameel/ramstack.htmxtoolkit/blob/main/samples/Ramstack.HtmxToolkit.Demo/Pages/Examples/Polling.cshtml) | [Polling recipe](recipes.md#poll-a-background-operation) |
+| Submit a protected form | [Antiforgery](https://github.com/rameel/ramstack.htmxtoolkit/blob/main/samples/Ramstack.HtmxToolkit.Demo/Pages/Examples/Antiforgery.cshtml) | [Antiforgery](antiforgery.md) |
## Inspect the browser behavior
diff --git a/docs/articles/tag-helpers.md b/docs/articles/tag-helpers.md
index a67f29e..a893c95 100644
--- a/docs/articles/tag-helpers.md
+++ b/docs/articles/tag-helpers.md
@@ -208,16 +208,16 @@ The output depends on the configured HTMX target: