| Title: | Build Modern Web Applications with 'htmx' and 'plumber2' |
|---|---|
| Description: | A lightweight framework for building server-driven web applications in 'R'. 'htmxr' combines the simplicity of 'htmx' for partial page updates with the power of 'plumber2' for non-blocking HTTP endpoints. Build interactive dashboards and data applications without writing 'JavaScript', using familiar 'R' patterns inspired by 'Shiny'. For more information on 'htmx', see <https://htmx.org>. |
| Authors: | Arthur Bréant [aut, cre] |
| Maintainer: | Arthur Bréant <[email protected]> |
| License: | MIT + file LICENSE |
| Version: | 0.3.1 |
| Built: | 2026-10-09 10:59:50 UTC |
| Source: | https://github.com/hyperverse-r/htmxr |
htmx serializerhtmxr registers a plumber2 serializer named htmx, which sends any
htmltools object as text/html. Use it on every route that returns
markup built with htmxr, whether that is a full page or a fragment:
#* @get /rows
#* @serializer htmx
function(query) {
hx_table_rows(diamonds, columns = c("cut", "price"))
}
The serializer plumber2 ships as html only understands a bare string or a
single htmltools::tags element. A tagList — what hx_table_rows() and
most fragment helpers return — matches neither, so it falls through to
plumber2's XML branch and is sent as unusable markup, with a 200 status
and no error. Reaching for @serializer none sidesteps that, but labels the
response text/plain.
htmx handles both, and always sets text/html:
tagList and single tags (as returned by hx_page(), hx_table_rows())
strings marked with htmltools::HTML()
plain character vectors, concatenated as-is
NULL, sent as an empty body — useful for a swap that clears its target
Anything else is an error rather than malformed markup. Content that is not htmltools output — an SVG device string, JSON — belongs on the matching plumber2 serializer instead.
The serializer is registered when htmxr is loaded, so library(htmxr) at
the top of your API file is enough to make @serializer htmx resolve.
Creates a <button> element with optional htmx attributes.
hx_button( id, label = NULL, class = NULL, get = NULL, post = NULL, put = NULL, patch = NULL, delete = NULL, target = NULL, swap = NULL, trigger = NULL, indicator = NULL, swap_oob = NULL, confirm = NULL, params = NULL, include = NULL, push_url = NULL, select = NULL, vals = NULL, encoding = NULL, headers = NULL, ... )hx_button( id, label = NULL, class = NULL, get = NULL, post = NULL, put = NULL, patch = NULL, delete = NULL, target = NULL, swap = NULL, trigger = NULL, indicator = NULL, swap_oob = NULL, confirm = NULL, params = NULL, include = NULL, push_url = NULL, select = NULL, vals = NULL, encoding = NULL, headers = NULL, ... )
id |
Element id. |
label |
Button label (text or HTML content). Pass |
class |
Optional CSS class(es). |
get |
URL for |
post |
URL for |
put |
URL for |
patch |
URL for |
delete |
URL for |
target |
CSS selector for |
swap |
Swap strategy for |
trigger |
Trigger specification for |
indicator |
CSS selector for |
swap_oob |
Out-of-band swap targets for |
confirm |
Confirmation message for |
params |
Parameters to submit for |
include |
CSS selector for |
push_url |
Push a URL into the browser history for |
select |
CSS selector for |
vals |
JSON string of extra values to include in the request for
|
encoding |
Encoding type for |
headers |
JSON string of request headers for |
... |
Additional HTML attributes passed to the |
An htmltools::tags object.
# Simple button hx_button("btn1", "Click me") # Button with htmx GET request hx_button("load-btn", "Load data", get = "/api/data", target = "#result") # Button with confirmation hx_button("del-btn", "Delete", post = "/api/delete", confirm = "Are you sure?")# Simple button hx_button("btn1", "Click me") # Button with htmx GET request hx_button("load-btn", "Load data", get = "/api/data", target = "#result") # Button with confirmation hx_button("del-btn", "Delete", post = "/api/delete", confirm = "Are you sure?")
Wraps tags to be included in the page head when passed to hx_page().
hx_head(..., title = "htmxr page")hx_head(..., title = "htmxr page")
... |
tags to include in the head (stylesheets, scripts, meta, etc.) |
title |
page title |
A list with class hx_head, to be passed to hx_page().
hx_head(title = "My app") hx_head( title = "My app", tags$link(rel = "stylesheet", href = "/style.css") )hx_head(title = "My app") hx_head( title = "My app", tags$link(rel = "stylesheet", href = "/style.css") )
Checks whether the incoming HTTP request was made by htmx by inspecting
the HX-Request header. htmx sends this header with every AJAX request.
hx_is_htmx(request)hx_is_htmx(request)
request |
A request object, typically the |
plumber2 requests are reqres::Request objects, which normalise header
names to lowercase snake_case: request$headers holds hx_request,
never hx-request. Their get_header() accessor handles that
normalisation itself and accepts the original HTTP name, so it is used
whenever it is available. When falling back to headers — for hand-built
lists in tests and examples — both hx-request and hx_request are
accepted.
TRUE if the request was made by htmx, FALSE otherwise.
# Simulated htmx request req <- list(headers = list(`hx-request` = "true")) hx_is_htmx(req) # Same request with the plumber2 header naming convention req <- list(headers = list(hx_request = "true")) hx_is_htmx(req) # Regular request req <- list(headers = list()) hx_is_htmx(req)# Simulated htmx request req <- list(headers = list(`hx-request` = "true")) hx_is_htmx(req) # Same request with the plumber2 header naming convention req <- list(headers = list(hx_request = "true")) hx_is_htmx(req) # Regular request req <- list(headers = list()) hx_is_htmx(req)
Generate a complete HTML page with htmx
hx_page(..., lang = "en", html_attrs = list())hx_page(..., lang = "en", html_attrs = list())
... |
page content. Use |
lang |
language code for the |
html_attrs |
a named list of additional attributes to set on the
|
A length-one character string containing the full HTML document
(including <!DOCTYPE html>), ready to be served as an HTTP response.
hx_page(tags$h1("Hello, htmxr!")) hx_page( hx_head(title = "My app"), tags$p("Hello, world!") )hx_page(tags$h1("Hello, htmxr!")) hx_page( hx_head(title = "My app"), tags$p("Hello, world!") )
Launches an example API that demonstrates htmxr features.
Call hx_run_example() without arguments to list available examples.
hx_run_example(example = NULL, port = 8080)hx_run_example(example = NULL, port = 8080)
example |
name of the example to run. If |
port |
port to run the API on. |
Called for side effects. When example is NULL, returns the
available example names invisibly. Otherwise does not return (the server
blocks).
hx_run_example() # list available examples if (interactive()) { hx_run_example("hello") # run the hello example }hx_run_example() # list available examples if (interactive()) { hx_run_example("hello") # run the hello example }
Creates a <select> element with optional htmx attributes.
When label is provided, the input is wrapped in a <div> containing a
<label> element linked via the for attribute.
hx_select_input( id, label = NULL, choices, selected = NULL, multiple = FALSE, name = id, class = NULL, get = NULL, post = NULL, put = NULL, patch = NULL, delete = NULL, target = NULL, swap = NULL, trigger = NULL, indicator = NULL, swap_oob = NULL, confirm = NULL, params = NULL, include = NULL, push_url = NULL, select = NULL, vals = NULL, encoding = NULL, headers = NULL, ... )hx_select_input( id, label = NULL, choices, selected = NULL, multiple = FALSE, name = id, class = NULL, get = NULL, post = NULL, put = NULL, patch = NULL, delete = NULL, target = NULL, swap = NULL, trigger = NULL, indicator = NULL, swap_oob = NULL, confirm = NULL, params = NULL, include = NULL, push_url = NULL, select = NULL, vals = NULL, encoding = NULL, headers = NULL, ... )
id |
Element id. Also used as |
label |
Optional label text. When provided, the input is wrapped in a
|
choices |
Named or unnamed character vector of choices. If unnamed, values are used as labels. If named, names are used as labels and values as option values (same convention as Shiny). |
selected |
Optional value(s) to pre-select. |
multiple |
Logical. If |
name |
Form field name. Defaults to |
class |
Optional CSS class(es) for the |
get |
URL for |
post |
URL for |
put |
URL for |
patch |
URL for |
delete |
URL for |
target |
CSS selector for |
swap |
Swap strategy for |
trigger |
Trigger specification for |
indicator |
CSS selector for |
swap_oob |
Out-of-band swap targets for |
confirm |
Confirmation message for |
params |
Parameters to submit for |
include |
CSS selector for |
push_url |
Push a URL into the browser history for |
select |
CSS selector for |
vals |
JSON string of extra values to include in the request for
|
encoding |
Encoding type for |
headers |
JSON string of request headers for |
... |
Additional HTML attributes passed to the |
An htmltools::tags object.
# Simple select without label hx_select_input("cut", choices = c("Fair", "Good", "Ideal")) # Select with label and named choices hx_select_input( "cut", label = "Filter by cut:", choices = c("All" = "all", "Fair", "Good", "Ideal"), selected = "all" ) # Select with htmx attributes hx_select_input( "cut", label = "Filter by cut:", choices = c("All" = "all", "Fair", "Good"), get = "/rows", trigger = "change", target = "#tbody" )# Simple select without label hx_select_input("cut", choices = c("Fair", "Good", "Ideal")) # Select with label and named choices hx_select_input( "cut", label = "Filter by cut:", choices = c("All" = "all", "Fair", "Good", "Ideal"), selected = "all" ) # Select with htmx attributes hx_select_input( "cut", label = "Filter by cut:", choices = c("All" = "all", "Fair", "Good"), get = "/rows", trigger = "change", target = "#tbody" )
Configures a plumber2 API to serve htmxr's static assets
(htmx JavaScript library) at /htmxr/assets/.
hx_serve_assets(api)hx_serve_assets(api)
api |
a plumber2 API object |
the API object (modified, for piping)
plumber2::api() |> hx_serve_assets()plumber2::api() |> hx_serve_assets()
A generic modifier that appends htmx attributes to an existing htmltools::tags object. Works with any HTML element.
hx_set( tag, get = NULL, post = NULL, put = NULL, patch = NULL, delete = NULL, target = NULL, swap = NULL, trigger = NULL, indicator = NULL, swap_oob = NULL, confirm = NULL, params = NULL, include = NULL, push_url = NULL, select = NULL, vals = NULL, encoding = NULL, headers = NULL, ... )hx_set( tag, get = NULL, post = NULL, put = NULL, patch = NULL, delete = NULL, target = NULL, swap = NULL, trigger = NULL, indicator = NULL, swap_oob = NULL, confirm = NULL, params = NULL, include = NULL, push_url = NULL, select = NULL, vals = NULL, encoding = NULL, headers = NULL, ... )
tag |
An htmltools::tags object to modify. |
get |
URL for |
post |
URL for |
put |
URL for |
patch |
URL for |
delete |
URL for |
target |
CSS selector for |
swap |
Swap strategy for |
trigger |
Trigger specification for |
indicator |
CSS selector for |
swap_oob |
Out-of-band swap targets for |
confirm |
Confirmation message for |
params |
Parameters to submit for |
include |
CSS selector for |
push_url |
Push a URL into the browser history for |
select |
CSS selector for |
vals |
JSON string of extra values to include in the request for
|
encoding |
Encoding type for |
headers |
JSON string of request headers for |
... |
Additional htmx attributes passed as-is (e.g.
|
The input tag with htmx attributes appended.
tags$div(id = "plot") |> hx_set(get = "/plot", trigger = "load", target = "#plot", swap = "innerHTML") hx_set( tags$div(id = "result", class = "container"), get = "/data", trigger = "load" )tags$div(id = "plot") |> hx_set(get = "/plot", trigger = "load", target = "#plot", swap = "innerHTML") hx_set( tags$div(id = "result", class = "container"), get = "/data", trigger = "load" )
Creates an <input type="range"> element with optional htmx attributes.
When label is provided, the input is wrapped in a <div> containing a
<label> element linked via the for attribute.
hx_slider_input( id, label = NULL, value = 50, min = 0, max = 100, step = 1, name = id, class = NULL, get = NULL, post = NULL, put = NULL, patch = NULL, delete = NULL, target = NULL, swap = NULL, trigger = NULL, indicator = NULL, swap_oob = NULL, confirm = NULL, params = NULL, include = NULL, push_url = NULL, select = NULL, vals = NULL, encoding = NULL, headers = NULL, ... )hx_slider_input( id, label = NULL, value = 50, min = 0, max = 100, step = 1, name = id, class = NULL, get = NULL, post = NULL, put = NULL, patch = NULL, delete = NULL, target = NULL, swap = NULL, trigger = NULL, indicator = NULL, swap_oob = NULL, confirm = NULL, params = NULL, include = NULL, push_url = NULL, select = NULL, vals = NULL, encoding = NULL, headers = NULL, ... )
id |
Element id. Also used as |
label |
Optional label text. When provided, the input is wrapped in a
|
value |
Initial value (default |
min |
Minimum value (default |
max |
Maximum value (default |
step |
Step increment (default |
name |
Form field name. Defaults to |
class |
Optional CSS class(es) for the |
get |
URL for |
post |
URL for |
put |
URL for |
patch |
URL for |
delete |
URL for |
target |
CSS selector for |
swap |
Swap strategy for |
trigger |
Trigger specification for |
indicator |
CSS selector for |
swap_oob |
Out-of-band swap targets for |
confirm |
Confirmation message for |
params |
Parameters to submit for |
include |
CSS selector for |
push_url |
Push a URL into the browser history for |
select |
CSS selector for |
vals |
JSON string of extra values to include in the request for
|
encoding |
Encoding type for |
headers |
JSON string of request headers for |
... |
Additional HTML attributes passed to the |
An htmltools::tags object.
# Simple slider hx_slider_input("bins", label = "Number of bins:", min = 1, max = 50) # Slider with htmx attributes hx_slider_input( "bins", label = "Number of bins:", value = 30, min = 1, max = 50, get = "/plot", trigger = "input changed delay:300ms", target = "#plot" )# Simple slider hx_slider_input("bins", label = "Number of bins:", min = 1, max = 50) # Slider with htmx attributes hx_slider_input( "bins", label = "Number of bins:", value = 30, min = 1, max = 50, get = "/plot", trigger = "input changed delay:300ms", target = "#plot" )
Builds a complete <table> element with a <thead> and a <tbody>.
htmx attributes are applied to the <tbody>, making it the swap target.
When data is NULL (the default), the <tbody> is empty and its content
is loaded lazily via htmx (e.g. trigger = "load").
hx_table( columns, data = NULL, tbody_id = NULL, col_labels = NULL, col_classes = NULL, class = NULL, thead_class = NULL, get = NULL, post = NULL, put = NULL, patch = NULL, delete = NULL, target = NULL, swap = NULL, trigger = NULL, indicator = NULL, swap_oob = NULL, confirm = NULL, params = NULL, include = NULL, push_url = NULL, select = NULL, vals = NULL, encoding = NULL, headers = NULL, ... )hx_table( columns, data = NULL, tbody_id = NULL, col_labels = NULL, col_classes = NULL, class = NULL, thead_class = NULL, get = NULL, post = NULL, put = NULL, patch = NULL, delete = NULL, target = NULL, swap = NULL, trigger = NULL, indicator = NULL, swap_oob = NULL, confirm = NULL, params = NULL, include = NULL, push_url = NULL, select = NULL, vals = NULL, encoding = NULL, headers = NULL, ... )
columns |
Character vector of column names to display. Defines the
|
data |
Optional data frame. If provided, rows are rendered in the
|
tbody_id |
|
col_labels |
Labels for the |
col_classes |
Named list of CSS classes for |
class |
CSS class(es) for the |
thead_class |
CSS class(es) for the |
get |
URL for |
post |
URL for |
put |
URL for |
patch |
URL for |
delete |
URL for |
target |
CSS selector for |
swap |
Swap strategy for |
trigger |
Trigger specification for |
indicator |
CSS selector for |
swap_oob |
Out-of-band swap targets for |
confirm |
Confirmation message for |
params |
Parameters to submit for |
include |
CSS selector for |
push_url |
Push a URL into the browser history for |
select |
CSS selector for |
vals |
JSON string of extra values for |
encoding |
Encoding type for |
headers |
JSON string of request headers for |
... |
Additional HTML attributes passed to the |
An htmltools::tags object (<table>).
# Lazy-load table (empty tbody, content loaded on trigger) hx_table( columns = c("cut", "color", "price"), col_labels = c("Cut", "Color", "Price"), tbody_id = "tbody", get = "/rows", trigger = "load", swap = "innerHTML" ) # Table with data pre-rendered df <- data.frame(cut = c("Fair", "Good"), price = c(326L, 400L)) hx_table(columns = c("cut", "price"), data = df)# Lazy-load table (empty tbody, content loaded on trigger) hx_table( columns = c("cut", "color", "price"), col_labels = c("Cut", "Color", "Price"), tbody_id = "tbody", get = "/rows", trigger = "load", swap = "innerHTML" ) # Table with data pre-rendered df <- data.frame(cut = c("Fair", "Good"), price = c(326L, 400L)) hx_table(columns = c("cut", "price"), data = df)
Converts a data frame into a tagList of <tr> elements, one per row.
Designed to be used as a fragment endpoint response — the output replaces
a <tbody> via htmx swap.
hx_table_rows(data, columns = NULL, col_classes = NULL)hx_table_rows(data, columns = NULL, col_classes = NULL)
data |
A data frame. |
columns |
Character vector of column names to include (and their order).
If |
col_classes |
Named list of CSS classes to add to |
A htmltools::tagList of <tr> tags.
Annotate the route with @serializer htmx. plumber2's own html
serializer does not understand a tagList and would send unusable markup.
See htmx-serializer.
df <- data.frame(cut = c("Fair", "Good"), price = c(326L, 400L)) hx_table_rows(df, columns = c("cut", "price")) # With CSS classes on specific columns hx_table_rows(df, col_classes = list(price = "text-end fw-bold"))df <- data.frame(cut = c("Fair", "Good"), price = c(326L, 400L)) hx_table_rows(df, columns = c("cut", "price")) # With CSS classes on specific columns hx_table_rows(df, col_classes = list(price = "text-end fw-bold"))
Adds an HX-Trigger, HX-Trigger-After-Swap, or HX-Trigger-After-Settle
header to a plumber2 response, causing htmx to fire one or more client-side
events after the response is received.
hx_trigger(response, event) hx_trigger_after_swap(response, event) hx_trigger_after_settle(response, event)hx_trigger(response, event) hx_trigger_after_swap(response, event) hx_trigger_after_settle(response, event)
response |
A plumber2 response object. The parameter must be named
|
event |
One of:
|
hx_trigger() — fires immediately when the response is received
(HX-Trigger).
hx_trigger_after_swap() — fires after htmx swaps the new content into
the DOM (HX-Trigger-After-Swap). Use this when the event handler needs
to interact with the freshly-swapped elements.
hx_trigger_after_settle() — fires after htmx settles (CSS transitions
complete) (HX-Trigger-After-Settle).
When event is a named list, values are serialised to JSON using a minimal
built-in serialiser that supports NULL, logicals, numbers, strings, and
named lists of the above. No external dependency required.
The response object response, invisibly.
## Not run: #* @post /submit function(response) { # Simple event hx_trigger(response, "formSubmitted") # Multiple events hx_trigger(response, c("formSubmitted", "refresh")) # Event with detail payload hx_trigger(response, list(showMessage = list(level = "info", text = "Saved!"))) list(status = "ok") } ## End(Not run)## Not run: #* @post /submit function(response) { # Simple event hx_trigger(response, "formSubmitted") # Multiple events hx_trigger(response, c("formSubmitted", "refresh")) # Event with detail payload hx_trigger(response, list(showMessage = list(level = "info", text = "Saved!"))) list(status = "ok") } ## End(Not run)