HTMX Compatibility & Migration Guide
Faststrap is designed around HTMX as its primary engine for reactive, zero-JavaScript user interfaces. Starting with Faststrap v0.8.2 and FastHTML 0.14.x, Faststrap provides a dual-engine architecture supporting both HTMX 2.x and HTMX 4.0.0.
The Dual-Engine Strategy
| Engine | Status | Default | Notes |
|---|---|---|---|
| HTMX 2.0.7 | Certified Stable | Yes | 100% backwards-compatible, production certified across all Faststrap components. |
| HTMX 4.0.0 | Forward Support (Opt-In) | No | Next-generation runtime featuring security isolation, native morphing, and modernized events. |
Faststrap guarantees that your existing apps will continue running smoothly on HTMX 2.0.7 without breaking changes. At the same time, you can opt into HTMX 4 today with a single parameter.
Configuring HTMX in Faststrap
You configure HTMX behavior when calling add_bootstrap() on your FastHTML app:
from fasthtml.common import FastHTML
from faststrap import add_bootstrap
app = FastHTML()
# Standard setup (uses certified HTMX 2.0.7 default)
add_bootstrap(app)
Opting into HTMX 4
To enable HTMX 4, pass htmx4=True:
add_bootstrap(
app,
htmx4=True,
htmx_compat=True, # Optional: load HTMX 2.x compatibility extension
allow_extensions=["response-targets", "sse"], # Whitelist allowed extensions (HTMX 4 security model)
)
Configuration Options
| Parameter | Type | Default | Description |
|---|---|---|---|
htmx4 |
bool | None |
None |
Explicitly enable or disable HTMX 4. When None, auto-detects if FastHTML app is configured with htmx4=True. |
htmx_compat |
bool |
False |
When htmx4=True, loads the official HTMX 4 2.x compatibility extension (hx-compat) to preserve legacy 2.x event and attribute naming. |
allow_extensions |
list[str] | None |
None |
Whitelist of extension names allowed to execute under HTMX 4's strict extension isolation policy. Configures htmx.config.allowExtensions. |
hx_config |
dict[str, Any] | None |
None |
Arbitrary key-value dictionary serialized into htmx.config via a <meta name="htmx-config"> tag. |
hx_version |
str | None |
None |
Override the HTMX CDN version string directly (e.g. "2.0.7" or "4.0.0"). |
The Cross-Version JavaScript Bridge (FaststrapHtmx)
HTMX 4 introduces modernized event lifecycles (such as htmx:swap replacing certain behaviors of htmx:afterSwap, and strict node processing).
Faststrap ships a lightweight client-side bridge (faststrap-htmx.js) that automatically loads ahead of component scripts. This bridge abstracts version differences:
// Register a callback that runs whenever new content is swapped into the DOM,
// regardless of whether HTMX 2 or HTMX 4 is running:
FaststrapHtmx.onSwap(function(targetElement) {
console.log("DOM updated:", targetElement);
});
// Check which engine is active:
if (FaststrapHtmx.isV4()) {
console.log("Running HTMX 4.x runtime");
}
// Process newly added dynamic nodes:
FaststrapHtmx.process(myContainer);
All Faststrap internal components (Bootstrap Toasts, ModernToast, Chart.js, GSAP animations, SplitPane, and OTP inputs) use this bridge, guaranteeing identical behavior across both engines.
Server-Side Patterns & Presets
Faststrap provides specialized presets designed to take advantage of multi-element updates and modern swap behaviors.
1. Multi-Target Updates (multi_response)
Under HTMX 4, multi-target swaps and OOB (out-of-band) patterns require explicit element boundaries. Faststrap provides multi_response to bundle a primary response with one or more out-of-band updates and an optional toast:
from faststrap.presets import multi_response
from faststrap import Card, StatCard, Badge
@app.post("/items/update")
def update_item():
primary = Card("Item updated successfully!")
stat_oob = StatCard(title="Active Items", value=42, id="sidebar-stat", hx_swap_oob="true")
badge_oob = Badge("Updated", id="header-status", hx_swap_oob="outerHTML:#header-status")
return multi_response(
primary,
stat_oob,
badge_oob,
toast=("Item status updated", "success"),
)
2. Native Morph Polling in DataTable
HTMX 4 supports native DOM morphing, while HTMX 2 uses the idiomorph extension. Faststrap's DataTable component abstracts this:
from faststrap import DataTable
DataTable(
data=my_live_records,
columns=["id", "name", "status"],
poll_interval=5, # Poll every 5 seconds
poll_morph=True, # Smoothly morph DOM changes without losing input focus or scroll position
poll_endpoint="/api/table-feed",
)
When poll_morph=True, the component sets hx_swap="morph" (or morph:outerHTML), preserving cell focus, expanded details, and scroll state across refreshes.
3. SSE Engines in SSETarget
SSETarget allows choosing between standard browser EventSource or HTMX SSE extensions:
from faststrap import SSETarget
# Default: lightweight native EventSource
SSETarget("/live-feed", sse_swap="notification", engine="eventsource")
# HTMX SSE extension engine
SSETarget("/live-feed", sse_swap="notification", engine="htmx")
4. Optimistic Pending State in LoadingButton
HTMX 4 provides optimistic UI updates with hx-pending. Faststrap's LoadingButton supports this via the pending parameter:
from faststrap.presets import LoadingButton
LoadingButton(
"Submit Application",
hx_post="/submit",
pending="Submitting your application...",
spinner=True,
)
Roadmap Note: LiveBind (P3)
HTMX 4 introduces experimental two-way bindings via hx-live. Faststrap's architectural review evaluated a LiveBind abstraction and determined that the specification is actively evolving across early 4.0 releases.
To ensure stability for production applications, LiveBind is deferred to HTMX 4.1+. Faststrap will introduce a dedicated reactive binding helper once the underlying HTMX 4.1 contract stabilizes.