This guide is for architects, platform engineers, and contributors who need to understand how htmx works internally, how to configure and secure it in production, and how the project is built and tested. It covers the library's architecture, the complete configuration surface, security model, build pipeline, and contribution workflow — written from the source at version 2.0.10.
The companion User Guide documents the
hx-*attribute vocabulary for application developers. This guide focuses on what runs under those attributes and how to operate htmx safely at scale.
1. Executive Summary
htmx is a client-side JavaScript library that generalizes the hypermedia model of HTML: it lets any element issue AJAX requests and swap the resulting HTML into the DOM, declaratively, via HTML attributes. There is no client-side application state to manage, no JSON serialization layer, and no build step.
| Property | Value |
|---|---|
| Version (this analysis) | 2.0.10 |
| Source commit | 5e867de (2026-06-01) |
| License | 0BSD (BSD Zero Clause — public-domain-equivalent) |
| Package name | htmx.org |
| Language | JavaScript (single file), with TypeScript definitions generated from JSDoc |
| Runtime dependencies | None |
| Distribution | CDN (unpkg, jsDelivr), npm, or self-hosted |
| Homepage | https://htmx.org/ |
Architectural thesis: htmx is a single ~5,600-line IIFE (immediately-invoked function expression) that registers a global event-delegation system. On load, it scans the DOM for hx-* attributes, wires up triggers, and intercepts the resulting events to perform XHR requests and DOM swaps. Everything else — history, indicators, out-of-band swaps, view transitions — is built on that core loop.
Note
For operators, the most important fact about htmx is that it shifts complexity to the server. There is no client build artifact to manage beyond a single static
.jsfile, but your server now returns HTML fragments and must understand theHX-*header protocol. Treat the server as the source of truth and the place where security boundaries are enforced.
2. Repository Structure
The repository contains the library itself, its documentation website, editor tooling, and an extensive test suite.
htmx/
├── src/
│ ├── htmx.js # The entire library (one IIFE, ~5,600 lines)
│ └── htmx.esm.d.ts # Hand-maintained / generated TypeScript definitions
├── dist/ # Build output (not in source tree; produced by scripts/dist.sh)
│ ├── htmx.js # UMD build
│ ├── htmx.min.js # Minified (CDN entry: jsdelivr/unpkg)
│ ├── htmx.esm.js # ES module (package "main")
│ └── htmx.esm.d.ts # Generated type declarations (package "types")
├── editors/
│ └── jetbrains/
│ └── htmx.web-types.json # Web-Types metadata for JetBrains IDE autocomplete
├── scripts/ # Build & release shell/Node scripts (dist.sh, www.sh, sha.sh, ...)
├── test/
│ ├── attributes/ # Per-attribute behavior tests (32 files)
│ ├── core/ # Core engine tests (17 files: api, ajax, events, ...)
│ ├── util/ # Test utilities
│ ├── lib/ # Vendored test libs (e.g. hyperscript)
│ └── manual/ # Browser-driven manual test pages (history, hx-boost, ...)
└── www/ # The htmx.org website (Hugo static site)
├── content/ # Markdown docs: attributes/, examples/, essays/, posts/, extensions/
├── templates/ # Hugo templates and shortcodes
├── themes/htmx-theme/ # The site theme
└── static/ # Static assets (and a mirrored copy of the test suite)
Important
The
www/static/test/tree is a mirror of the top-leveltest/directory, served by the website so tests can be run in the browser at htmx.org. When contributing, edit the canonicaltest/files — the static copy is generated/synced, not authored directly.
| Directory | Role | Audience |
|---|---|---|
src/ |
The library source — the only file that ships | Contributors |
dist/ |
Build artifacts consumed by users | Release |
test/ |
Automated test suite (Mocha + Chai, run via web-test-runner) | Contributors |
www/ |
Documentation site (htmx.org) | Docs contributors |
editors/ |
IDE integration metadata | Tooling |
scripts/ |
Build, type-generation, and release automation | Maintainers |
3. Architecture Deep Dive
3.1 The IIFE and the public object
The entire library is wrapped in var htmx = (function() { 'use strict'; ... })(). Inside, a single public object is constructed and returned. Its shape (from the source) is:
htmx = {
// Event processing
onLoad, process, on, off, trigger, ajax,
// DOM querying helpers
find, findAll, closest, values,
// DOM manipulation helpers
remove, addClass, removeClass, toggleClass, takeClass, swap,
// Extension entry points
defineExtension, removeExtension,
// Debugging
logAll, logNone, logger,
// Configuration
config: { ... },
// Misc
parseInterval, location, _ (internalEval), version
}
A quirk worth knowing for contributors: the public methods are declared as null in the object literal and reassigned immediately afterward (htmx.process = processNode, etc.). The inline comment explains this is a TypeScript-compiler workaround — assigning functions directly produced invalid .d.ts output, but reassignment is fine.
3.2 The internal API
A second object, internalAPI, is exposed to extensions (via the init(api) callback). It surfaces lower-level functions that extensions need but that are not part of the stable public surface:
internalAPI = {
addTriggerHandler, bodyContains, canAccessLocalStorage, findThisElement,
filterValues, swap, hasAttribute, getAttributeValue, getClosestAttributeValue,
getClosestMatch, getExpressionVars, getHeaders, getInputValues, getInternalData,
getSwapSpecification, getTriggerSpecs, getTarget, makeFragment, mergeObjects,
makeSettleInfo, oobSwap, querySelectorExt, settleImmediately, shouldCancel,
triggerEvent, triggerErrorEvent, withExtensions
}
Warning
The
internalAPIis intentionally not documented as a public contract. Extensions that reach into it are coupling to htmx internals and may break across minor versions. Prefer the documented extension hooks (onEvent,transformResponse,handleSwap,isInlineSwap,encodeParameters,getSelectors).
3.3 Node processing and per-element state
When htmx processes a node (processNode), it:
- Reads
hx-trigger(or infers a default trigger from the element type). - Parses trigger specifications into structured objects (
HtmxTriggerSpecification). - Registers event listeners that, when fired, issue the AJAX request.
- Computes an attribute hash (
attributeHash) so it can detect when an element's attributes change and avoid re-initializing unchanged nodes.
Per-element private state (in-flight XHR, timeouts, listener registrations, polling state, the request queue, etc.) is stored on the element under a non-enumerable htmx-internal-data property, retrieved via getInternalData(elt). This keeps htmx's bookkeeping off the public DOM surface.
3.4 Attribute inheritance
Almost every hx-* attribute is inherited by descendants. Resolution walks up the ancestor chain (getClosestMatch / getClosestAttributeValue) until it finds the attribute. Two attributes modulate this:
hx-disinherit="<attrs>"— a parent declares which attributes its descendants should not inherit.hx-inherit="<attrs>"— used whenhtmx.config.disableInheritanceistrueto opt specific attributes back into inheritance.
Setting htmx.config.disableInheritance = true flips the model from opt-out to opt-in across the whole page.
3.5 The request → swap pipeline
issueAjaxRequest(verb, path, elt, event)
├─ resolve target (hx-target) and swap spec (hx-swap)
├─ gather input values (form data, hx-include, hx-params filter, hx-vals)
├─ assemble headers (getHeaders → HX-Request, HX-Trigger, HX-Target, ...)
├─ htmx:confirm / htmx:configRequest / htmx:beforeRequest (cancelable)
├─ open XMLHttpRequest, add htmx-request class
├─ on response → handleAjaxResponse:
│ ├─ honor response headers (HX-Location, HX-Redirect, HX-Refresh,
│ │ HX-Retarget, HX-Reswap, HX-Reselect, HX-Trigger*)
│ ├─ resolve response handling (status code → swap? error?)
│ ├─ htmx:beforeSwap (can re-target / rewrite content / cancel)
│ ├─ saveCurrentPageToHistory (if a history update is pending)
│ ├─ swap(target, content, swapSpec) → swapWithStyle dispatch
│ │ ├─ OOB swaps (hx-swap-oob)
│ │ ├─ title update, scroll/show, focus & selection preservation
│ │ └─ settle phase (htmx-settling, attribute settling)
│ └─ push/replace URL into history (if configured)
└─ htmx:afterRequest, remove htmx-request class
3.6 Swap-style dispatch
swapWithStyle() is a switch over the swap style. The native styles are none, outerHTML, afterbegin, beforebegin, beforeend, afterend, delete, and innerHTML (the default fallthrough). textContent is handled earlier in swap() (it inserts text without HTML parsing). Any unrecognized swap style is offered to registered extensions' handleSwap hooks; if no extension claims it, htmx falls back to htmx.config.defaultSwapStyle. This is the extension point used by morphing extensions (e.g. Idiomorph).
3.7 Response handling configuration
The status-code → behavior mapping is data-driven via htmx.config.responseHandling, which defaults to:
responseHandling: [
{ code: '204', swap: false }, // No Content: never swap
{ code: '[23]..', swap: true }, // 2xx/3xx: swap
{ code: '[45]..', swap: false, error: true } // 4xx/5xx: error, no swap
]
Each entry can additionally specify target, select, swapOverride, ignoreTitle, and a client event to fire. This lets operators define application-wide policies (e.g. "swap 422 validation responses into the form") without per-element attributes.
Tip
Customizing
responseHandlingis the cleanest way to make error responses (like422 Unprocessable Entityfrom server-side form validation) swap their body into the page, instead of being silently dropped as errors.
4. Configuration Reference
All runtime configuration lives on htmx.config. Set it in a script after htmx loads, or declaratively with a meta tag:
<meta name="htmx-config" content='{"defaultSwapStyle":"outerHTML","timeout":5000}'>
The following table is the complete config surface with the exact defaults from the source.
| Key | Type | Default | Purpose |
|---|---|---|---|
historyEnabled |
boolean | true |
Enable browser-history support |
historyCacheSize |
number | 10 |
Pages kept in sessionStorage for history restore |
refreshOnHistoryMiss |
boolean | false |
On a history cache miss, do a full reload instead of an AJAX fetch |
defaultSwapStyle |
string | 'innerHTML' |
Swap style when hx-swap is omitted |
defaultSwapDelay |
number | 0 |
ms between response and swap |
defaultSettleDelay |
number | 20 |
ms between swap and settle |
includeIndicatorStyles |
boolean | true |
Inject CSS so .htmx-indicator elements hide until a request runs |
indicatorClass |
string | 'htmx-indicator' |
Class revealed during requests |
requestClass |
string | 'htmx-request' |
Class added to the triggering element in-flight |
addedClass |
string | 'htmx-added' |
Class briefly applied to newly-added nodes |
settlingClass |
string | 'htmx-settling' |
Class on targets during settle |
swappingClass |
string | 'htmx-swapping' |
Class on targets during swap |
allowEval |
boolean | true |
Allow eval-based features (hx-vars, trigger filters, js: vals) |
allowScriptTags |
boolean | true |
Execute <script> tags in swapped content |
inlineScriptNonce |
string | '' |
CSP nonce added to inline scripts htmx re-inserts |
inlineStyleNonce |
string | '' |
CSP nonce added to inline styles |
attributesToSettle |
string[] | ['class','style','width','height'] |
Attributes transitioned during settle |
withCredentials |
boolean | false |
Send cookies/auth headers on cross-site requests |
timeout |
number | 0 |
Request timeout in ms (0 = no timeout) |
wsReconnectDelay |
string | fn | 'full-jitter' |
WebSocket reconnect backoff strategy |
wsBinaryType |
string | 'blob' |
Binary type for WebSocket data |
disableSelector |
string | '[hx-disable], [data-hx-disable]' |
Selector that disables htmx on a subtree |
scrollBehavior |
string | 'instant' |
Behavior for show/scroll (auto/instant/smooth) |
defaultFocusScroll |
boolean | false |
Scroll focused elements into view after a swap |
getCacheBusterParam |
boolean | false |
Add a cache-buster param to GET requests |
globalViewTransitions |
boolean | false |
Use the View Transitions API for all swaps |
methodsThatUseUrlParams |
string[] | ['get','delete'] |
Verbs that encode params in the URL |
selfRequestsOnly |
boolean | true |
Block requests to non-origin hosts |
ignoreTitle |
boolean | false |
Never update document.title from responses |
scrollIntoViewOnBoost |
boolean | true |
Scroll a boosted target into view |
triggerSpecsCache |
object | null | null |
Optional cache for parsed trigger specs |
disableInheritance |
boolean | false |
Flip attribute inheritance to opt-in |
responseHandling |
array | (see §3.7) | Status-code → swap/error policy |
allowNestedOobSwaps |
boolean | true |
Process OOB swaps nested inside the main response |
historyRestoreAsHxRequest |
boolean | true |
Send HX-Request on history-miss full reloads |
reportValidityOfForms |
boolean | false |
Report HTML5 validation errors and focus the first invalid input |
Note
Several defaults changed between htmx 1.x and 2.x. The most operationally significant is
selfRequestsOnlynow defaults totrue(it wasfalsein 1.x), hardening the default posture against cross-origin request abuse.
5. Security Analysis
htmx executes server-returned HTML in the browser, so the trust boundary is the server response. The library's security posture is governed primarily by a handful of config flags.
5.1 Threat model
| Concern | Mechanism | Default posture |
|---|---|---|
| Cross-origin request abuse | selfRequestsOnly |
Blocked by default (true) |
eval / code injection |
allowEval |
Enabled (true) — needed for hx-vars, trigger filters, js: vals |
| Script execution in responses | allowScriptTags |
Enabled (true) — <script> in swapped HTML runs |
| CSP compatibility | inlineScriptNonce, inlineStyleNonce |
Empty — set to your per-request nonce |
| Credential leakage | withCredentials |
Disabled (true sends cookies cross-site) |
| Sensitive data in history cache | hx-history="false" |
Per-page opt-out |
5.2 Content Security Policy (CSP)
htmx is CSP-friendly but requires configuration:
script-srcwithoutunsafe-eval: sethtmx.config.allowEval = false. This disableshx-vars, square-bracket trigger event filters, andjs:/javascript:prefixes inhx-vals/hx-headers. When eval is disallowed, htmx fires anhtmx:evalDisallowedErrorevent rather than evaluating.- Nonce-based inline scripts/styles: set
inlineScriptNonce/inlineStyleNonceto the nonce your server emits per response. htmx copies the nonce onto any inline<script>/<style>it re-inserts during a swap (it must re-create script nodes because some browsers won't execute scripts inserted from<template>content).
Caution
allowScriptTagsdefaults totrue, meaning any<script>tag in a swapped response will execute. If your server can ever reflect untrusted user content into an htmx response, you have a stored-XSS vector. Either rigorously sanitize/escape on the server, or sethtmx.config.allowScriptTags = falseto strip scripts from swapped content.
5.3 Cross-origin requests
With selfRequestsOnly = true (the default), htmx validates the request URL against the page origin and refuses non-same-origin requests, firing htmx:invalidPath/htmx:badResponseUrl paths as appropriate. Only relax this if you have a deliberate cross-origin integration, and pair it with a strict server-side CORS policy.
5.4 The HX-* header protocol as a security tool
Because htmx sends HX-Request: true on every request, servers can:
- Distinguish AJAX fragment requests from full-page navigations and avoid leaking partial templates to direct URL access.
- Use
HX-Redirect/HX-Locationto enforce auth redirects from the server without trusting client logic.
Tip
Validate the
HX-*request headers server-side but never trust them for authorization — they are set by the client and can be forged. Use them for response shaping (fragment vs. full page), not for access control. Authorization must use your normal session/token mechanisms.
5.5 Known community-reported issues
From the project's issue tracker at the analyzed commit, security-relevant or behavior-edge issues to be aware of:
| Issue | Summary |
|---|---|
| #3743 | Nested OOB swap performed even when nested OOB is intended to be off (interacts with allowNestedOobSwaps) |
| #3189 | With HX-Redirect, the hx-indicator element is not deactivated |
| #2131 | WebSocket: handling events with hx-on reported broken in an older version |
These are functional edge cases rather than vulnerabilities, but operators relying on OOB-swap nesting behavior or redirect-with-indicator flows should test against them.
6. Build & Development Workflow
htmx ships a single source file but has a small Node-based toolchain for distribution, type generation, and the website. All commands are npm scripts defined in package.json.
6.1 Prerequisites
| Tool | Why |
|---|---|
| Node.js + npm | Run scripts, ESLint, TypeScript, uglify |
| Playwright browsers | Headless Chromium/Firefox/WebKit for the test suite |
| Bash | Several scripts (dist.sh, www.sh, sha.sh) are shell scripts |
git clone https://github.com/bigskysoftware/htmx.git
cd htmx
npm install
added N packages in Xs
6.2 Key npm scripts
| Script | Command (effect) |
|---|---|
npm run dist |
scripts/dist.sh → build dist/, then generate types and web-types |
npm run lint |
ESLint over src/htmx.js, test/, and scripts/*.mjs (config standard + mocha) |
npm run format |
ESLint --fix over the same files |
npm run types-check |
tsc type-check of src/htmx.js (--checkJs, no emit) |
npm run types-generate |
Generate dist/htmx.esm.d.ts from the built ESM file |
npm run web-types-generate |
node scripts/generate-web-types.mjs → JetBrains web-types JSON |
npm test |
lint + types-check + test:chrome |
npm run test:chrome / test:firefox / test:webkit |
Run the suite in a single browser via Playwright |
npm run test:all |
Run across Chromium, Firefox, and WebKit |
npm run test:ci |
lint + types-check + test:all (the full gate) |
npm run www |
bash scripts/www.sh → build/serve the documentation site |
npm run sha / update-sha |
Compute / update integrity SHAs for releases |
Important
htmx is authored in plain JavaScript with JSDoc annotations, and TypeScript runs in check-only mode (
--checkJs) against the source. The shipped.d.tsis generated from the built ESM file, not hand-written from scratch. When you change a public function signature, update its JSDoc — that is the source of truth for the generated types.
6.3 ESLint configuration
The project extends eslint-config-standard plus plugin:mocha/recommended, with a deliberately relaxed rule set (many no-* rules disabled) reflecting htmx's terse, performance-oriented style. Two rules are notable:
space-before-function-paren: ['error', 'never']— function declarations have no space before the paren.no-restricted-propertiesbansString.prototype.substrin favor of.slice/.substring.
7. Testing Strategy
htmx has an unusually thorough browser-based test suite, which is a key reason it can ship a single un-modularized file with confidence.
| Aspect | Detail |
|---|---|
| Frameworks | Mocha (runner) + Chai / chai-dom (assertions) + Sinon (spies/stubs) |
| Execution | @web/test-runner driving real browsers via Playwright |
| Browsers | Chromium, Firefox, WebKit |
| WebSocket tests | mock-socket for unit tests; a real ws server (test/ws-sse/server.js) for integration (npm run ws-tests) |
| Layout | test/attributes/ (one file per hx-* attribute), test/core/ (engine: ajax, api, events, history, regressions), test/manual/ (human-driven browser pages for history & boost edge cases) |
Tip
The
test/manual/pages exist because some behaviors — browser history restoration,hx-boostnavigation, Safari/iOS history quirks — are difficult to assert in a headless runner. When debugging history or boost issues, open these pages in a real browser; they reproduce the exact scenarios the maintainers use.
To run the full suite the way CI does:
npm run test:ci
> lint && types-check && test:all
... ESLint passes
... tsc type-check passes
... web-test-runner: N tests passed across chromium, firefox, webkit
8. Deployment & Operations
htmx itself is a static asset; "deployment" means how you serve and version it.
8.1 Serving strategies
| Strategy | Trade-offs |
|---|---|
Public CDN (unpkg/jsDelivr) |
Zero hosting; relies on third-party availability and trust. Pin exact versions. |
| Self-hosted minified file | Full control, same-origin, CSP-simple. You own cache headers and updates. |
| Bundled (npm import) | Integrates with your build; ensure window.htmx is set if you use inline attributes. |
8.2 Operational checklist
- [ ] Pin an exact htmx version (avoid floating
@latest/ major-only tags). - [ ] Add Subresource Integrity (
integrity+crossorigin) if loading from a CDN. - [ ] Decide and set the security flags:
allowEval,allowScriptTags,selfRequestsOnly, nonces. - [ ] Define a
responseHandlingpolicy for error/validation status codes. - [ ] Ensure the server branches on the
HX-Requestheader (fragment vs. full page). - [ ] Add
hx-history="false"to any page caching sensitive data insessionStorage. - [ ] Set a
timeoutif your backend can hang, and handlehtmx:timeout. - [ ] Establish a CSP and verify htmx features still work under it.
Warning
If you serve htmx from a CDN without Subresource Integrity, a compromise of that CDN would let an attacker run arbitrary JavaScript on every page that includes htmx. SRI hashes (which the
npm run shascript helps generate) pin the exact bytes you trust.
8.3 Performance characteristics
- htmx adds work proportional to the number of
hx-*-annotated elements processed on load and after each swap. It caches attribute hashes to skip unchanged nodes. - Trigger spec parsing can be cached via
htmx.config.triggerSpecsCacheif you have many elements sharing identicalhx-triggerstrings. - The library uses event delegation and
XMLHttpRequest; there is no virtual DOM diffing overhead. The cost model is "parse a fragment, do native DOM insertion, settle a few attributes."
9. Editor & Tooling Integration
editors/jetbrains/htmx.web-types.json provides Web-Types metadata so JetBrains IDEs (WebStorm, IntelliJ, PyCharm) offer autocomplete and documentation for hx-* attributes. It is generated by scripts/generate-web-types.mjs and published as the package's web-types field, so consumers who npm install htmx.org get editor support automatically. For VS Code, equivalent support is provided by community extensions consuming the same metadata.
10. Extending htmx
Extensions are first-class. Register one with htmx.defineExtension(name, partialExtension); activate it on a subtree with hx-ext="name".
The extension interface (defaults shown by extensionBase()):
| Hook | Signature | Purpose |
|---|---|---|
init(api) |
(internalAPI) => void |
Receive the internal API on registration |
onEvent(name, evt) |
(string, CustomEvent) => boolean |
Observe/veto any htmx event (return false to cancel) |
transformResponse(text, xhr, elt) |
=> string |
Rewrite the raw response before swapping |
isInlineSwap(swapStyle) |
=> boolean |
Declare a custom swap style as inline |
handleSwap(swapStyle, target, fragment, settleInfo) |
=> boolean | Node[] |
Implement a custom swap style (e.g. morph) |
encodeParameters(xhr, params, elt) |
=> any | string | null |
Custom request body encoding |
getSelectors() |
=> string[] | null |
Extra selectors htmx should process |
In htmx 2, formerly-core features such as WebSockets (ws), Server-Sent Events (sse), and class-based response targeting moved into official extensions, keeping the core library small.
htmx.defineExtension('uppercase', {
transformResponse: function (text /*, xhr, elt */) {
return text.toUpperCase()
}
})
11. Code Quality & Conventions
| Observation | Detail |
|---|---|
| Single-file design | The whole library is one IIFE in src/htmx.js. This is intentional — it keeps the distributable a single small file and avoids a bundler. |
| Defensive DOM access | Helpers like asElement, asHtmlElement, asParentNode, and getInternalData guard against null/wrong-type nodes throughout. |
| Type safety via JSDoc | Extensive JSDoc @param/@returns/@typedef annotations drive tsc --checkJs and the generated .d.ts. |
| Terse style | Many lint rules are relaxed; the code favors compactness. Contributors should match the surrounding style and run npm run format. |
| Shadow DOM aware | getRootNode({composed}), parentElt (handles ShadowRoot), and extended selectors (host, root) show deliberate shadow-DOM support. |
| Browser-compat workarounds | E.g. normalizeScriptTags re-creates <script> nodes because some browsers won't execute scripts inserted from <template> content. |
12. Potential Improvements & Technical Debt
These are observations for maintainers and downstream forks, derived from the source and issue tracker — not defects in normal operation.
- Single-file size: at ~5,600 lines the library is large for one file. Internal modularization (kept bundled for distribution) could improve contributor onboarding, at the cost of a build step the project currently avoids by design.
evalsurface:allowEval-gated features (hx-vars, trigger filters,js:vals) are a recurring CSP friction point. Documenting a fully eval-free usage path prominently would help locked-down deployments.- OOB-swap nesting (#3743): the interaction between
allowNestedOobSwapsand nested OOB elements has a reported surprising case worth a regression test and doc clarification. - Indicator lifecycle on redirect (#3189):
hx-indicatornot deactivating onHX-Redirectis a UX rough edge. - Release artifact consistency (#3765, #3553): community reports of CDN/GitHub release artifact mismatches suggest the release pipeline (
scripts/) could benefit from tighter automation/verification.
13. Quick Reference
Security flags at a glance
| Flag | Harden by setting |
|---|---|
allowEval |
false (strict CSP, no unsafe-eval) |
allowScriptTags |
false (strip scripts from swapped HTML) |
selfRequestsOnly |
keep true (default) |
withCredentials |
keep false unless cross-site auth is required |
inlineScriptNonce / inlineStyleNonce |
your per-request CSP nonce |
Request headers htmx sends
HX-Request, HX-Trigger, HX-Trigger-Name, HX-Target, HX-Current-URL, HX-Prompt, HX-Boosted, HX-History-Restore-Request
Response headers htmx honors
HX-Location, HX-Redirect, HX-Refresh, HX-Push-Url, HX-Replace-Url, HX-Retarget, HX-Reswap, HX-Reselect, HX-Trigger, HX-Trigger-After-Swap, HX-Trigger-After-Settle
Build/test commands
npm install # install dev toolchain
npm run dist # build dist/ + types + web-types
npm test # lint + type-check + chrome tests
npm run test:ci # full gate: lint + type-check + all browsers
npm run www # build/serve the docs site