sherpa carries your data between R Shiny and Alpine.js. It lets you build highly reactive user interfaces by synchronizing reactive values between a Shiny backend and an Alpine.js Frontend.
Alpine.js is a rugged, minimal framework for composing behavior directly in your markup. It is the perfect partner for Shiny because it is great at handling the "last mile" of the user experience and integrates nicely into Shiny and the Shiny development workflow. Alpine is a lightweight dependency that does not require a build step or any kind of tooling. It's there when you need it but doesn't try to take center stage. It gives you "just enough JavaScript".
In a standard Shiny app, the server often manages micro-interactions and things like disabling a button or toggling a class can be surprisingly cumbersome to implement. Sherpa's goal is to take some of that weight from Shiny's shoulders, so Shiny can do what it does best: heavy-duty data processing, complex business logic, and deep integration with the R ecosystem while Alpine.js handles the "fast-twitch" reflexes: UI state, smooth transitions, and client-side interactions that happen at the speed of thought.
-
✅ Synced Stores: Create a store in R with store(). It behaves like a native reactiveValues list but automatically mirrors its state to Alpine.js in the browser.
-
✅ Bi-Directional Sync: Change a value in R, and the UI updates. Change a value in Alpine (via x-model or logic), and R sees the change immediately.
-
✅ R-Native Directives: All core Alpine.js directives (
x-data,x-model,x-for, etc.) and some of the official plugins available as standard R functions with support for modifiers and optional arguments. Gives you a sense of IntelliSense when writing Alpine Directives in Shiny UI Code. Directive helpers are complemented bys$, a "Sherpa-aware" version ofshiny::tagsthat automatically handles Alpine directive helpers without the need for backticks or the!!!splice-operator. -
✅ Support for HTML-Templates, so you can write your Alpine.js code in an HTML file and leverage the power of IntelliSense. Plus: there's an experimental Live-Server with HMR, that renders your changes instantly while maintaining the current state.
-
📋 TODO: Auto-Binding: Automatically handles Shiny input/output binding for dynamically generated Alpine content.
-
📋 TODO: Supports Alpine.js Plugins, with special accomodations for Alpine AJAX
-
📋 TODO: Examples of Sherpa in use
You can install the development version of sherpa like this:
devtools::install_github("teebusch/sherpa")Use the s$ proxy for Shiny-Tags to easily inject Alpine directives into your Shiny-UI.
library(shiny)
library(sherpa)
ui <- fluidPage(
use_alpine(),
s$div(
class = "p-5",
x_data("{ count: 0 }"),
s$h2("Counter: ", s$span(x_text("count"))),
s$button("Increment", x_click("count++"), class = "btn btn-primary")
)
)
server <- function(input, output, session) {}
shinyApp(ui, server)library(shiny)
library(sherpa)
ui <- fluidPage(
use_alpine(stores = "app"),
s$div(
x_data(),
s$h3("Status: ", s$span(x_text("$store.app.status"))),
s$ul(
s$template(
x_for("task in $store.app.tasks"),
s$li(x_text("task"))
)
),
actionButton("add", "Add Task from R")
)
)
server <- function(input, output, session) {
# Create a synced S3 store
app_state <- store("app", data = list(
status = "Waiting...",
tasks = list("Learn Alpine", "Install Sherpa")
))
# Update the store directly - Alpine mirrors this instantly!
observeEvent(input$add, {
app_state$status <- "Updating..."
app_state$tasks <- c(app_state$tasks, paste("New Task", Sys.time()))
})
}
shinyApp(ui, server)-
Use the Proxy: Use s$tag_name() (e.g., s$div()) instead of tags$div() when you want to pass x_ helpers. Or, if you prefer, use the Sherpa operator
%s% -
No Bangs: Because of the
s$proxy, you do not need the!!!operator. Just pass the helper:s$div(x_data(NA)). -
Declare Stores: Pass your store names to use_alpine(stores = c("myStore")) to prevent "undefined" errors during page load.
| R Helper | Alpine directive | What it does |
|---|---|---|
x_data() |
x-data |
Defines a component and its reactive data. |
x_bind() |
x-bind |
Dynamically binds HTML attributes (e.g. class, disabled). |
x_on() |
x-on |
Listens for browser events (shortcut: x_click, x_change). |
x_text() |
x-text |
Updates the inner text of an element dynamically. |
x_html() |
x-html |
Updates the inner HTML of an element (use with caution). |
x_model() |
x-model |
Creates two-way data binding on input elements. |
x_modelable() |
x-modelable |
Expose Alpine properties as the target of x-model. |
x_show() |
x-show |
Toggles visibility via CSS (display: none). |
x_transition() |
x-transition |
Applies smooth CSS transitions for entries and exits. |
x_for() |
x-for |
Loops over data to create elements (must use <template>). |
x_if() |
x-if |
Conditionally adds/removes elements (must use <template>). |
x_init() |
x-init |
Runs JavaScript code when the element is initialized. |
x_effect() |
x-effect |
Runs a script when a reactive dependency changes. |
x_ref() |
x-ref |
Utility for accessing DOM-Elements directly. |
x_cloak() |
x-cloak |
Hides elements until Alpine has finished loading. |
x_ignore |
x-cloak |
Lets Alpine ignore an element. |
Use use_alpine(plugins = c(<PLUGIN_NAMES>)) to activate.
| R Helper | Alpine directive | What it does | Plugin |
|---|---|---|---|
x_intersect() |
x-intersect |
React when an element enters the viewport. | intersect |
x_sort() |
x-sort |
Re-order elements by dragging them with your mouse. | sort |
x_collapse() |
x-collapse |
Expand and collapse elements. | collapse |
x_mask() |
x-mask |
Automatically format input fields (phone, dates, etc.). | mask |
x_trap() |
x-trap |
Trap keyboard focus within an element (for modals). | focus |
x_anchor() |
x-anchor |
Position element relative to a reference element. | anchor |
x_resize() |
x-resize |
Execute JavaScript whenever an element's size changes. | resize |
Directive missing? Use x_attr_builder() to create your own.
See the Alpine Docs for more details
| R Helper | Alpine Magic | What it does |
|---|---|---|
s_store() |
$store |
Access global Alpine stores. |
Use use_alpine(plugins = c(<PLUGIN_NAMES>)) to activate.
| R Helper | Alpine Magic | What it does | Plugin |
|---|---|---|---|
s_persist() |
$persist |
persist Alpine state across page loads | persist |
See the Alpine Docs for more details
