The hardware and bandwidth for this mirror is donated by dogado GmbH, the Webhosting and Full Service-Cloud Provider. Check out our Wordpress Tutorial.
If you wish to report a bug, or if you are interested in having us mirror your free-software or open-source project, please feel free to contact us at mirror[@]dogado.de.
htmx is a lightweight JavaScript library (~16kb) that lets any HTML element send HTTP requests — not just <a> and <form> tags.
The core philosophy is HTML over the wire: your server returns HTML fragments, not JSON. The browser swaps those fragments directly into the page without a full reload.
htmxr is the R wrapper: it provides htmltools-based primitives to generate htmx attributes and build complete pages, backed by a plumber2 server.
install.packages("htmxr")
# development version
pak::pak("hyperverse-r/htmxr")htmxr uses plumber2 as its HTTP server — make sure it is installed alongside htmxr.
Every htmx interaction follows the same four-step cycle:
You control this cycle through five core attributes:
| Attribute | htmxr parameter | What it does |
|---|---|---|
hx-get |
get = "/url" |
Send GET request on trigger |
hx-post |
post = "/url" |
Send POST request on trigger |
hx-target |
target = "#id" |
CSS selector of the element to update |
hx-swap |
swap = "innerHTML" |
How to insert the response (innerHTML, outerHTML…) |
hx-trigger |
trigger = "click" |
What triggers the request (click, change, load…) |
In htmxr, these map directly to function parameters — no JavaScript to write.
The fastest way to see htmxr in action is to run the built-in hello example:
library(htmxr)
hx_run_example("hello")This launches an Old Faithful histogram where a slider controls the number of bins. Let’s walk through how it works.
The page is served by a GET / route. hx_page() wraps the full HTML document and injects the htmx script automatically. hx_head() handles the <head> tag.
The slider is built with hx_slider_input(). Three htmx parameters connect it to the server:
hx_slider_input(
id = "bins",
label = "Number of bins:",
value = 30,
min = 1,
max = 50,
get = "/plot", # send GET /plot on trigger
trigger = "input changed delay:300ms", # trigger: input event, debounced 300ms
target = "#plot" # replace the content of #plot
)The plot container is a plain <div> with an id. hx_set() adds htmx attributes to it so the plot loads immediately on page load:
tags$div(id = "plot") |>
hx_set(
get = "/plot",
trigger = "load", # fires once when the element is loaded
target = "#plot",
swap = "innerHTML"
)The /plot route returns an SVG string — an HTML fragment, not a full page:
#* @get /plot
#* @query bins:integer(30)
#* @parser none
#* @serializer none
function(query) {
generate_plot(query$bins)
}When the slider moves, htmxr sends GET /plot?bins=35. The server returns the SVG. htmx swaps it into #plot. No JavaScript, no JSON parsing, no manual DOM manipulation.
slider input event
│
▼
GET /plot?bins=35 ──► server renders SVG
│
◄──────────────┘
htmx swaps SVG into #plot
A minimal htmxr app needs only two things:
api.R — your plumber2 API with two kinds of routes:
GET / — returns the full page (built with hx_page())
GET /fragment — returns HTML fragments (one route per dynamic piece)
hx_serve_assets() — registers the htmx JavaScript file as a static asset on your plumber2 router. hx_page() and hx_head() handle injecting the <script> tag automatically.
# Minimal api.R structure
library(htmxr)
#* @get /
#* @serializer html
function() {
hx_page(
hx_head(
title = "My app"
),
tags$div(
id = "content"
) |>
hx_set(
get = "/content",
trigger = "load",
target = "#content"
)
)
}
#* @get /content
#* @serializer html
function() {
tags$p("Hello from the server!")
}Launch API with:
library(htmxr)
pr <- plumber2::api("api.R") |>
hx_serve_assets()
cat("\n🚀 Launch API...\n")
cat("🌐 Webapp: http://127.0.0.1:8080/\n\n")
pr$ignite(port = 8080)
Explore more built-in examples:
# List all available examples
hx_run_example()
# Dynamic table filtering with hx_select_input()
hx_run_example("select-input")These binaries (installable software) and packages are in development.
They may not be fully stable and should be used with caution. We make no claims about them.
Health stats visible at Monitor.