htmx 4 Preview

Opt into Chirp's exact htmx 4 preview bundle, validate self-hosted assets, and roll back safely

Page actions AI-ready formats and sharing
Open LLM text
Share with AI
Ask Claude Ask ChatGPT Ask Gemini Ask Copilot

Opt in exactly

Chirp's htmx 4 lane is provisional and never selected by a loose range. Reuse the existing frozen config fields with the one allowlisted pin:

from chirp import App, AppConfig

app = App(AppConfig(htmx=True, htmx_version="4.0.0-beta5"))

Chirp injects these classic deferred scripts once, in order:

  1. dist/htmx.min.js
  2. dist/ext/htmx-2-compat.min.js
  3. dist/ext/hx-sse.min.js

Every URL is pinned tohtmx.org@4.0.0-beta5; every tag receives the same request CSP nonce and exact tier/version metadata. A core marker already in the document suppresses the entire managed bundle, not just the first script.

Self-host the complete bundle

Sethtmx=Falseand own all three local files. Mark their roles so app.check()and DevTools can distinguish a complete preview from a silent mixed client:

<meta name="htmx-config"
  content='{"noSwap":[204,304,"5xx"],"defaultTimeout":60000,"compat":{"swapErrorResponseCodes":true}}'
  data-chirp="htmx-config" data-chirp-htmx-tier="4-preview"
  data-chirp-htmx-version="4.0.0-beta5">
<script defer src="/static/htmx.min.js"
  data-chirp="htmx" data-chirp-htmx-role="core"
  data-chirp-htmx-tier="4-preview"
  data-chirp-htmx-version="4.0.0-beta5"></script>
<script defer src="/static/htmx-2-compat.min.js"
  data-chirp="htmx-extension" data-chirp-htmx-extension="compat"
  data-chirp-htmx-tier="4-preview"
  data-chirp-htmx-version="4.0.0-beta5"></script>
<script defer src="/static/hx-sse.min.js"
  data-chirp="htmx-extension" data-chirp-htmx-extension="sse"
  data-chirp-htmx-tier="4-preview"
  data-chirp-htmx-version="4.0.0-beta5"></script>

Local artifact integrity remains your deployment responsibility. Chirp does not add a configurable CDN base or infer extension versions.

Browser-default contract

The preview does not inherit material upstream defaults accidentally:

Surface Chirp preview contract
Inheritance htmx-2-compat temporarily restores implicit inheritance; app.check() warns so templates can move to :inherited.
Error swaps 4xx HTML swaps, includingValidationError 422 fragments. 5xx does not swap unless a local hx-status:5xxtarget opts in.
OOB Main content swaps first, then OOB/partial tasks in document order. Regions must be independent.
DELETE Enclosing form fields are excluded. Usehx-include="closest form"; beta 5 sends included DELETE values as query parameters.
History Back/forward refetches server HTML. Declare one stablehx-history-eltshell boundary when pushing URLs.
Timeout Ordinary requests time out after 60 seconds. UseStream, Suspense, or EventStream for long-lived work, or a local hx-configtimeout.
Queueing queue:* trigger modifiers are errors. Use hx-syncwith an explicit queue strategy.
SSE hx-sse:connect uses one fetch stream. Rendered Fragments are unnamed HTML; explicit targets use validated <hx-partial> envelopes. Named SSEEvents are DOM events.

The preview also rejectsHX-Trigger-After-Swapand HX-Trigger-After-Settlebefore send because htmx 4 ignores them. Keep HX-Triggerfor receipt-phase events. For post-mutation timing, render escaped data in the target block and read it from an external htmx:before:settle/htmx:after:settlelistener.

Managed injection emits the marked policy metadata before core. A self-hosted preview must copy that exact meta tag before its three scripts;app.check() rejects a missing, late, duplicated, or mismatched policy.

Fail before the browser

Thehtmx_compatibility contract category reports both ERROR and WARNING. Every template-drift diagnostic names the selected tier, exact construct, consequence, remediation, template, and detected line.

AnERRORmeans the selected browser tier cannot safely execute the markup: managed and manual core would load twice, preview roles are incomplete or mixed, versions disagree, htmx 4-only attributes are paired with htmx 2, legacy SSE/WebSocket attributes are paired with htmx 4, or an old event has no compatibility mapping. The htmx 2hx-disableattribute is also an error in preview because htmx 4 gives that name a different meaning; rename it to hx-ignorebefore upgrading.

AWARNING identifies migration debt that the provisioned htmx-2-compat extension keeps working temporarily: renamed/removed htmx 2 attributes, old core lifecycle event names, old config keys, and implicit inheritance. For example, move an inheritedhx-confirm to hx-confirm:inheritedbefore removing compatibility mode.

Static checks ignore markup inside<pre>/<code>, JavaScript comments, framework-owned templates, and dynamic attribute bundles. The optional pinned upstream inventory command is documented in docs/audits/htmx4-beta5-inventory.md; it does not add Node to Chirp's runtime dependencies.

In debug mode,window.ChirpHtmxDebug.getHtmxCompatibility()reports configured and live versions, extension roles, source URLs, duplicates, the declared/live client policy, and the resulting compatibility state. Request records include the resolvedhx-syncowner and strategy. Request headers alone do not prove the browser version.

Roll back

Return to the verified baseline and remove htmx 4-only markup:

app = App(AppConfig(htmx=True, htmx_version="2.0.10"))

Run app.check()before rollout. Chirp's default and generated scaffolds stay on 2.0.10 until the separate htmx 4 GA release gate is satisfied.