Skip to main content

Intro

Demo

jsx
function MyComponent({ children }) {
return (
"<!DOCTYPE html>" +
(
<html lang="en">
<head>
<title>My Website</title>
</head>
<body hx-boost hx-ext="sse">
Type '{ "hx-boost": true; "hx-ext": "sse"; }' is not assignable to type 'HtmlBodyTag'. Types of property '["hx-boost"]' are incompatible. Type 'true' is not assignable to type 'BoolStr | undefined'.2322Type '{ "hx-boost": true; "hx-ext": "sse"; }' is not assignable to type 'HtmlBodyTag'. Types of property '["hx-boost"]' are incompatible. Type 'true' is not assignable to type 'BoolStr | undefined'.
<div id="main">
{children}
<form
hx-post="/purchase"
hx-swap="outerHTML"
hx-vals={{ foo: "bar" }}
>
<button type="submit">Buy Now</button>
</form>
</div>
</body>
</html>
)
);
}
jsx
function MyComponent({ children }) {
return (
"<!DOCTYPE html>" +
(
<html lang="en">
<head>
<title>My Website</title>
</head>
<body hx-boost hx-ext="sse">
Type '{ "hx-boost": true; "hx-ext": "sse"; }' is not assignable to type 'HtmlBodyTag'. Types of property '["hx-boost"]' are incompatible. Type 'true' is not assignable to type 'BoolStr | undefined'.2322Type '{ "hx-boost": true; "hx-ext": "sse"; }' is not assignable to type 'HtmlBodyTag'. Types of property '["hx-boost"]' are incompatible. Type 'true' is not assignable to type 'BoolStr | undefined'.
<div id="main">
{children}
<form
hx-post="/purchase"
hx-swap="outerHTML"
hx-vals={{ foo: "bar" }}
>
<button type="submit">Buy Now</button>
</form>
</div>
</body>
</html>
)
);
}

Install

shell
npm i typed-htmx
shell
npm i typed-htmx

You can also install as a dev dependency if you're only using the type definitions.

Usage

You can configure typed-htmx either as pure type declarations, or as a JSX templating engine.

As type declarations

Configure your tsconfig.json as follows:

jsonc
{
"compilerOptions": {
"jsx": "react",
"moduleResolution": "node16", // or "nodenext"
"types": ["typed-htmx" /** and any other types you need */]
}
}
jsonc
{
"compilerOptions": {
"jsx": "react",
"moduleResolution": "node16", // or "nodenext"
"types": ["typed-htmx" /** and any other types you need */]
}
}

An alternative is to include a triple-slash directive wherever you need completions for htmx attributes:

jsx
/// <reference types="typed-htmx" />
 
function MyComponent({ children }) {
return <div hx-get="/asd">{children}</div>;
(property) HtmxAttributes["hx-get"]?: string | undefined
}
jsx
/// <reference types="typed-htmx" />
 
function MyComponent({ children }) {
return <div hx-get="/asd">{children}</div>;
(property) HtmxAttributes["hx-get"]?: string | undefined
}

If your frontend library injects its own JSX types, you'll need to augment it. See the example project for a demo. typed-html and React are supported out of the box.

As a JSX templating engine

If you prefer to use JSX only for its templating capabilities in the vein of typed-html, you can use typed-htmx/typed-html which is included with this library and optimized for htmx usage:

  • Attributes such as hx-vals and hx-headers may also accept an object literal, which will be stringified on demand.
  • Configurable options for sanitization, defaults to a no-op.

Configure your tsconfig.json as follows:

jsonc
{
"compilerOptions": {
"jsx": "react-jsx",
"jsxImportSource": "typed-htmx/typed-html",
"moduleResolution": "node16" // or "nodenext"
}
}
jsonc
{
"compilerOptions": {
"jsx": "react-jsx",
"jsxImportSource": "typed-htmx/typed-html",
"moduleResolution": "node16" // or "nodenext"
}
}

Tips

Configuring the JSX runtime

If you don't have any other JSX runtimes like React or Preact set up, you can use typed-htmx/typed-html, which will convert JSX into strings at runtime. You can configure the runtime using jsxConfig:

js
import { jsxConfig } from "typed-htmx";
// Set to true to allow all text and skip sanitization
jsxConfig.trusted = true;
// Bring your own sanitizer
jsxConfig.sanitize = (raw, originalType) => `..`;
js
import { jsxConfig } from "typed-htmx";
// Set to true to allow all text and skip sanitization
jsxConfig.trusted = true;
// Bring your own sanitizer
jsxConfig.sanitize = (raw, originalType) => `..`;

Compiling JSX templates

JSX functions are fairly fast and will unlikely to be a bottleneck in your server. However, it is possible to achieve higher performance via techniques such as code transformations à la Babel. For example, you can use swc-plugin-static-jsx which will transform the demo snippet into pure string interpolation:

ts
// Use typed-htmx's template function
import { html } from "typed-htmx";
 
// Or provide your own
function myHtml(raw: TemplateStringsArray, ...args: unknown[]): string {
return `..`;
}
 
function MyComponent({ children }) {
return (
"<!DOCTYPE html>" +
html`<html lang="en">
<head>
<title>My Website</title>
</head>
<body hx-boost hx-ext="sse">
<div id="main">
${{ $$child: children }}
<form
hx-post="/purchase"
hx-swap="outerHTML"
${{ "hx-vals": { foo: "bar" } }}
>
<button type="submit">Buy Now</button>
</form>
</div>
</body>
</html>`
);
}
ts
// Use typed-htmx's template function
import { html } from "typed-htmx";
 
// Or provide your own
function myHtml(raw: TemplateStringsArray, ...args: unknown[]): string {
return `..`;
}
 
function MyComponent({ children }) {
return (
"<!DOCTYPE html>" +
html`<html lang="en">
<head>
<title>My Website</title>
</head>
<body hx-boost hx-ext="sse">
<div id="main">
${{ $$child: children }}
<form
hx-post="/purchase"
hx-swap="outerHTML"
${{ "hx-vals": { foo: "bar" } }}
>
<button type="submit">Buy Now</button>
</form>
</div>
</body>
</html>`
);
}