Attribute Values and Infrastructure
This section documents supporting attribute value types and infrastructure for the HTMX DSL. These types handle specialized data encoding, configuration, and type-class infrastructure that enables extensibility.
Attribute Value Types
These types enable specialized data encoding and configuration:
HxVals — Custom Values
HxVals represents the hx-vals attribute, allowing you to send custom data alongside form parameters. It accepts both schema-backed values (automatically JSON-encoded) and raw JSON:
import zio.http.htmx._
import zio.blocks.schema._
import zio.blocks.schema.json.Json
// From a schema-backed value
case class Extra(userId: Int, role: String)
HxVals.from(Extra(123, "admin"))
// From raw JSON
HxVals.json(Json.Object(Chunk("userId" -> Json.Number(123))))
Use HxVals.from[T](value) with an implicit Schema[T] to automatically encode any data type to JSON. First, set up a schema for your data type:
import zio.blocks.html._
import zio.http.htmx._
import zio.blocks.schema._
case class RequestContext(userId: Int, sessionId: String)
object RequestContext {
implicit val schema: Schema[RequestContext] = Schema.derived
}
Then use it in your form:
import zio.blocks.html._
import zio.http.htmx._
form(
hxPost := "/api/action",
hxVals := HxVals.from(RequestContext(42, "abc123")),
button("Submit")
)
Or use HxVals.json(json) when you already have JSON. Set up the imports first:
import zio.blocks.schema.json.Json
import zio.blocks.chunk.Chunk
Then construct the JSON:
import zio.blocks.html._
import zio.http.htmx._
import zio.blocks.schema.json.Json
import zio.blocks.chunk.Chunk
form(
hxPost := "/api/action",
hxVals := HxVals.json(Json.Object(Chunk("priority" -> Json.String("high")))),
button("Submit")
)
HxHeadersValue — Custom Headers
HxHeadersValue represents the hx-headers attribute, sending custom HTTP headers with HTMX requests. Like HxVals, it accepts schema-backed values or raw JSON:
import zio.http.htmx._
import zio.blocks.schema.json.Json
import zio.blocks.chunk.Chunk
// From raw JSON object
val example1 = HxHeadersValue.json(Json.Object(Chunk(
"X-Custom-Header" -> Json.String("value"),
"X-Request-ID" -> Json.String("12345")
)))
// From a schema-backed value
import zio.blocks.schema._
case class Headers(traceId: String)
object Headers {
implicit val schema: Schema[Headers] = Schema.derived
}
val example2 = HxHeadersValue.from(Headers("xyz789"))
Add custom headers to all requests within an element:
import zio.blocks.html._
import zio.http.htmx._
import zio.blocks.schema.json.Json
import zio.blocks.chunk.Chunk
div(
hxHeaders := HxHeadersValue.json(Json.Object(Chunk(
"X-API-Key" -> Json.String("secret123"),
"X-Client-Version" -> Json.String("2.0")
))),
button(hxPost := "/action", "Request (with custom headers)")
)
HxSwapOob — Out-of-Bounds Swaps
HxSwapOob represents the hx-swap-oob attribute, enabling out-of-bounds swaps that update content outside the primary request target. This allows a single response to update multiple DOM regions simultaneously.
Use HxSwapOob(true) or HxSwapOob(false) for boolean flags:
import zio.blocks.html._
import zio.http.htmx._
div(
id := "status-badge",
hxSwapOob := HxSwapOob(true),
"Ready"
)
Or use HxSwapOob.using(swap) to specify a swap strategy:
import zio.blocks.html._
import zio.http.htmx._
div(
id := "notification",
hxSwapOob := HxSwapOob.using(HxSwap.BeforeEnd),
span("New notification")
)
Combine with a target selector:
import zio.blocks.html._
import zio.http.htmx._
div(
hxSwapOob := HxSwapOob.using(
HxSwap.InnerHTML,
HxTarget.css("#status")
),
"Updated Status"
)
HxExtensions — HTMX Extensions
HxExtensions represents the hx-ext attribute, enabling HTMX extensions. Pass extension names as comma-separated values:
import zio.http.htmx._
// Enable multiple extensions
HxExtensions("json-enc", "class-tools")
Use in elements to opt in to extensions:
import zio.blocks.html._
import zio.http.htmx._
form(
hxPost := "/api/submit",
hxExt := HxExtensions("json-enc", "debug"),
button("Submit")
)
The hxExt attribute key provides a convenience builder to construct extensions:
import zio.blocks.html._
import zio.http.htmx._
form(
hxExt("json-enc", "class-tools"),
button("Submit")
)
HxAttributeNames — Attribute Disinheritance
HxAttributeNames represents the hx-disinherit attribute, preventing child elements from inheriting specific HTMX attributes from their ancestors. Pass attribute names as space-separated values:
import zio.http.htmx._
// Prevent inheriting these attributes
HxAttributeNames("hx-trigger", "hx-swap")
Use to override inherited attributes:
import zio.blocks.html._
import zio.http.htmx._
div(
hxPost := "/parent-action",
hxTrigger := HxTrigger.click,
button(
hxDisinherit := HxAttributeNames("hx-post", "hx-trigger"),
hxPost := "/child-action",
hxTrigger := HxTrigger.submit,
"Child (overrides parent)"
)
)
The hxDisinherit attribute key provides a convenience builder:
import zio.blocks.html._
import zio.http.htmx._
div(
hxPost := "/parent-action",
button(
hxDisinherit("hx-post", "hx-trigger"),
hxPost := "/child-action",
"Child"
)
)
Infrastructure Types
These types provide the internal machinery for the HTMX DSL:
HtmxAttrKey — Typed Attribute Keys
HtmxAttrKey[-A] is the typed attribute key binding an attribute name to its expected value type. Unlike plain HTML attributes, HTMX attributes carry compile-time type information, preventing type mismatches.
The HtmxAttributes mixin provides predefined keys like hxPost, hxSwap, hxTarget, etc.; use these rather than constructing HtmxAttrKey directly. Each key enforces the correct value type:
import zio.blocks.html._
import zio.http.htmx._
// hxSwap expects HxSwap; this compiles
div(hxSwap := HxSwap.InnerHTML)
// hxTarget expects HxTarget; this compiles
div(hxTarget := HxTarget.This)
// hxPost expects UrlLike (String | Path | URL); this compiles
div(hxPost := "/api/endpoint")
ToHtmxValue — Type Class for Rendering
ToHtmxValue[-A] is the type class that renders HTMX domain values to attribute strings. It's automatically derived for types like HxSwap, HxTarget, HxTrigger, and basic primitives.
The module provides instances for common types out of the box:
- Primitives:
String,Boolean,Int,Long,Doublerender to their string representations - Selectors:
CssSelectorrenders via itsrendermethod - URLs:
PathandURLrender to encoded strings - HTMX domain types:
HxSwap,HxTarget,HxTrigger,HxParams, etc. render via theirrendermethods - Schema-backed types:
Schema[A]instances enableHxVals.from()to encode any data to JSON
Extending with Custom Types
Define your own ToHtmxValue instance to make custom domain types work in the HTMX DSL:
import zio.http.htmx._
sealed trait Priority
object Priority {
case object Low extends Priority
case object Medium extends Priority
case object High extends Priority
implicit val toHtmxValue: ToHtmxValue[Priority] = new ToHtmxValue[Priority] {
def toHtmxValue(value: Priority): String = value match {
case Low => "low"
case Medium => "medium"
case High => "high"
}
}
}
Use Priority values directly in HTMX attributes through the ToHtmxValue type class:
import zio.blocks.html._
import zio.http.htmx._
button(
hxPost := "/api/task",
hxOn.click := Js(s"console.log('Priority: ${Priority.High.toString()}')"),
"Create High Priority Task"
)
Implicit Resolution
When you assign a value to an HTMX attribute, the type class is summoned implicitly:
import zio.blocks.html._
import zio.http.htmx._
// Implicit ToHtmxValue[HxSwap] is summoned
div(hxSwap := HxSwap.InnerHTML)
// Implicit ToHtmxValue[String] is summoned
div(hxPost := "/endpoint")
If you get an error like "No ToHtmxValue instance found for type X," implement the type class for your custom type as shown above.
Integration
These types work together to provide a complete, extensible HTMX DSL. For example, a form might use multiple attribute value types:
import zio.blocks.html._
import zio.http.htmx._
import zio.blocks.schema.json.Json
import zio.blocks.chunk.Chunk
import scala.concurrent.duration._
form(
hxPost := "/api/submit",
hxTrigger := HxTrigger.submit,
hxTarget := HxTarget.closest("form"),
hxSwap := HxSwap.InnerHTML.settle(250.millis),
hxParams := HxParams.only("name", "email"),
hxEncoding := HxEncoding.Multipart,
hxHeaders := HxHeadersValue.json(Json.Object(Chunk(
"X-Request-ID" -> Json.String("12345")
))),
hxVals := HxVals.from(RequestContext(42, "session-abc")),
button("Submit")
)
Each attribute key is type-safe, and the ToHtmxValue type class ensures values render correctly to HTMX syntax.