Build interactive user interfaces for 'shiny' applications
through a conversation with a large language model (LLM).
Developers choose a set of reusable components, and the model arranges
and updates those components as the user describes what they need.
Each component's inputs are checked before it is shown, and the model
supplies data rather than executable code. Applications can also save and
replay the sequence of interface changes without contacting a model.
For background on generative user interfaces, see
Leviathan et al. (2026)

shinygenui lets you add generative UI to Shiny apps. As the app developer, you choose a fixed set of UI components and describe the arguments that each component accepts. Together, these components form a catalog. When someone asks a question in the chat panel, the model chooses components from the catalog and adds them to a canvas. It can also update or remove components as the conversation continues.
The package is similar to Vercel's json-render and Google's A2UI, but has a much smaller scope. It uses R, Shiny, and the deployment approaches you already use for Shiny apps. You do not need Node, another server, or schemas written by hand.
In this recording, the model turns requests into an interactive mtcars dataset
explorer by choosing components from the app's catalog, adding them to the canvas,
and updating them as the conversation continues.
You can install shinygenui from CRAN:
install.packages("shinygenui")
Or try the development version from GitHub:
# install.packages("pak")
pak::pak("nanxstats/shinygenui")
eval() or parse() on model output.Here is a complete app with a chat sidebar, a canvas, and a catalog based on
mtcars. Start by asking for a plot of mpg against hp and a value box with
the average mpg. Then ask the model to color the points by cylinder count.
It updates the existing plot in place.
Create a gitignored .env file alongside the app and set all three values
explicitly:
OPENAI_API_KEY=<your-api-key>
SHINYGENUI_MODEL=<model-id>
SHINYGENUI_EFFORT=<reasoning-effort>
readRenviron(".env")
library(shiny)
library(bslib)
library(shinygenui)
ui <- page_sidebar(
title = "mtcars explorer",
sidebar = sidebar(
width = 380,
shinychat::chat_ui("chat", height = "100%")
),
genui_canvas("canvas")
)
server <- function(input, output, session) {
catalog <- genui_catalog(genui_components_bslib(data = mtcars))
chat <- ellmer::chat_openai(
model = Sys.getenv("SHINYGENUI_MODEL"),
params = ellmer::params(
reasoning_effort = Sys.getenv("SHINYGENUI_EFFORT")
),
echo = "none"
)
genui_server(
"canvas",
catalog = catalog,
chat = chat, # Any ellmer provider works
data = reactive(mtcars),
chat_id = "chat",
system_prompt = genui_prompt(catalog, context = "The data is mtcars.")
)
}
shinyApp(ui, server)
The included components have the same interactive behavior as any other Shiny UI. For example, the histogram has a slider for the number of bins. Moving the slider redraws the plot immediately without another request to the model.
You can define your own component with genui_component():
genui_component(
name = "value_box",
description = "A box highlighting one summary statistic.",
args = list(
title = "Short label above the value.",
column = ellmer::type_enum(names(mtcars), "Column to summarize.")
),
ui = function(id, args) {
...
}, # Return htmltools tags
server = function(id, args, data) {
...
} # Optional Shiny module
)
The inst/examples/ directory contains two runnable apps:
readRenviron(".env")
shiny::runApp(system.file("examples/01-mtcars-explorer/", package = "shinygenui"))
shiny::runApp(system.file("examples/02-layout/", package = "shinygenui"))
01-mtcars-explorer introduces the basics. 02-layout shows how to
group components in rows and rebuild a canvas from its saved history with
genui_replay().
vignette("shinygenui") explains catalogs, argument validation, updates,
interactive components, and replay.DESIGN.md documents the architecture.MIT