@varavel/nodx-htmx
v1.0.0
Published
HTMX attributes for NodX TypeScript
Maintainers
Readme
HTMX integration for NodX TypeScript.
This package gives you type-safe helpers for every HTMX attribute. Instead of writing attribute strings by hand, you call a function and get an ordinary NodX attribute node that can be passed to any element.
Installation
# Deno (JSR)
deno add --save-exact jsr:@varavel/nodx-htmx
# Node / Bun / Deno via npm
npm install --save-exact @varavel/nodx-htmxThis package requires jsr:@varavel/nodx@^1.0.0 (or npm:@varavel/nodx) as a peer. If you already
use NodX, you already have it.
Quick Start
import { Button, Div, Text } from "@varavel/nodx";
import { HxGet, HxSwap, HxTarget, HxTrigger } from "@varavel/nodx-htmx";
const button = Button(
HxGet("/api/data"),
HxTarget("#result"),
HxSwap("outerHTML"),
HxTrigger("click"),
Text("Load data"),
);
console.log(button.render());<button
hx-get="/api/data"
hx-target="#result"
hx-swap="outerHTML"
hx-trigger="click"
>
Load data
</button>NodX handles escaping and rendering. HTMX takes over in the browser.
You can also use a namespace import if you prefer:
import * as N from "@varavel/nodx";
import * as Htmx from "@varavel/nodx-htmx";
const node = N.Div(
Htmx.HxGet("/api/data"),
Htmx.HxTarget("#content"),
"Load",
);Why this package
Writing HTMX attributes as raw strings works, but it is easy to mistype hx-boost or hx-trigger
and you get no help from the editor. These helpers are tiny wrappers around Attr("hx-...") that
keep the usual NodX flow:
- You stay in TypeScript. No template strings, no manual escaping.
- You get autocompletion for every HTMX attribute.
- The output is exactly the HTML HTMX expects.
- It works the same in Deno, Node, Bun, and browsers.
Usage
Basic request attributes
The most common HTMX attributes are the HTTP verbs:
import { Div } from "@varavel/nodx";
import { HxDelete, HxGet, HxPatch, HxPost, HxPut } from "@varavel/nodx-htmx";
console.log(Div(HxGet("/api/data")).render());
// <div hx-get="/api/data"></div>
console.log(Div(HxPost("/api/data")).render());
// <div hx-post="/api/data"></div>
console.log(Div(HxPut("/api/data")).render());
// <div hx-put="/api/data"></div>
console.log(Div(HxDelete("/api/data")).render());
// <div hx-delete="/api/data"></div>Target and swap control
Control what gets swapped and how:
import { Div } from "@varavel/nodx";
import { HxGet, HxSelect, HxSwap, HxTarget, HxTrigger } from "@varavel/nodx-htmx";
const node = Div(
HxGet("/api/data"),
HxTarget("#content"),
HxSelect("#content"),
HxSwap("outerHTML"),
HxTrigger("click"),
"Load",
);
console.log(node.render());
// <div hx-get="/api/data" hx-target="#content" hx-select="#content" hx-swap="outerHTML" hx-trigger="click">Load</div>Event handling with hx-on
import { Div } from "@varavel/nodx";
import { HxOn } from "@varavel/nodx-htmx";
const node = Div(HxOn("click", "alert('hi')"));
console.log(node.render());
// <div hx-on:click="alert('hi')"></div>
// HTMX events work the same way
const afterRequest = Div(HxOn("htmx:afterRequest", "console.log(event)"));
console.log(afterRequest.render());
// <div hx-on:htmx:afterRequest="console.log(event)"></div>Combining directives
HTMX attributes compose like any other NodX attribute. Order is preserved.
import { Div } from "@varavel/nodx";
import { HxBoost, HxConfirm, HxGet, HxIndicator, HxPushURL } from "@varavel/nodx-htmx";
const card = Div(
HxGet("/api/card"),
HxPushURL("true"),
HxBoost("true"),
HxConfirm("Are you sure?"),
HxIndicator("#spinner"),
"Card content",
);
console.log(card.render());
// <div hx-get="/api/card" hx-push-url="true" hx-boost="true" hx-confirm="Are you sure?" hx-indicator="#spinner">Card content</div>With other NodX attributes
HTMX helpers return real NodX attribute nodes, so they work side by side with Class, Id, and the
rest:
import { Class, Div, Id } from "@varavel/nodx";
import { HxGet } from "@varavel/nodx-htmx";
const node = Div(
Id("app"),
Class("p-4"),
HxGet("/api/data"),
"Content",
);
console.log(node.render());
// <div id="app" class="p-4" hx-get="/api/data">Content</div>Escape hatch
Hx lets you build any hx- attribute that does not have a dedicated helper, or handle future HTMX
additions:
import { Div } from "@varavel/nodx";
import { Hx } from "@varavel/nodx-htmx";
const node = Div(Hx("get", "/api/data"));
console.log(node.render());
// <div hx-get="/api/data"></div>
const custom = Div(Hx("custom-attr", "value"));
console.log(custom.render());
// <div hx-custom-attr="value"></div>Available Functions
All helpers return a NodX Node, so they can be used anywhere you would use Attr, Class or
Id.
| Function | HTMX docs |
| ------------------------ | --------------------------------------------------------------- |
| Hx(key, value) | Generic hx-[key] |
| HxGet(value) | hx-get |
| HxPost(value) | hx-post |
| HxPut(value) | hx-put |
| HxPatch(value) | hx-patch |
| HxDelete(value) | hx-delete |
| HxOn(eventName, value) | hx-on |
| HxPushURL(value) | hx-push-url |
| HxSelect(value) | hx-select |
| HxSelectOOB(value) | hx-select-oob |
| HxSwap(value) | hx-swap |
| HxSwapOOB(value) | hx-swap-oob |
| HxTarget(value) | hx-target |
| HxTrigger(value) | hx-trigger |
| HxVals(value) | hx-vals |
| HxBoost(value) | hx-boost |
| HxConfirm(value) | hx-confirm |
| HxDisable(value) | hx-disable |
| HxDisabledELT(value) | hx-disabled-elt |
| HxDisinherit(value) | hx-disinherit |
| HxEncoding(value) | hx-encoding |
| HxExt(value) | hx-ext |
| HxHeaders(value) | hx-headers |
| HxHistory(value) | hx-history |
| HxHistoryElt(value) | hx-history-elt |
| HxInclude(value) | hx-include |
| HxIndicator(value) | hx-indicator |
| HxInherit(value) | hx-inherit |
| HxParams(value) | hx-params |
| HxPreserve(value) | hx-preserve |
| HxPrompt(value) | hx-prompt |
| HxReplaceURL(value) | hx-replace-url |
| HxRequest(value) | hx-request |
| HxSync(value) | hx-sync |
| HxValidate(value) | hx-validate |
| HxVars(value) | hx-vars |
How it works
Each helper is a thin wrapper around Attr("hx-...", value) from @varavel/nodx. For example,
HxGet is essentially:
function HxGet(value) {
return Attr("hx-get", value);
}HxOn adds the colon part:
function HxOn(eventName, value) {
return Attr(`hx-on:${eventName}`, value);
}Because the result is a real NodX attribute node, it participates in normal element rendering: attributes are collected in insertion order and escaped automatically. Numbers are supported too; they are stringified before escaping.
import { Div } from "@varavel/nodx";
import { HxGet } from "@varavel/nodx-htmx";
console.log(Div(HxGet(42)).render());
// <div hx-get="42"></div>No server code is involved. The same helpers work in Deno, Node, Bun, or any other JavaScript runtime because they only generate attributes. If you need to read HTMX request headers on the server, do it directly with whatever server framework you use.
Documentation
Full API reference with examples for every function is available on JSR:
https://jsr.io/@varavel/nodx-htmx/doc
If you want to browse the signatures or check a specific attribute quickly, start there.
License
MIT - see LICENSE.
