rtui provides 35+ widgets and 9 layout containers. This guide covers every one with practical examples you can copy and run.
Terminal only: All examples in this guide must be saved as
.Rfiles and run from a real terminal withRscript my_app.R. rtui apps do not work in RStudio, R GUI, Jupyter, or any embedded R console. See Getting Started for setup instructions.
How layouts work
Layouts are built by nesting container functions. Each container accepts child widgets as arguments and arranges them according to its rules:
library(rtui)
# Vertical stack with a horizontal row inside
quick_app(
title = "Layout Demo",
layout = vstack(
header(),
text("Top section", id = "top"),
hstack(
text("Left", id = "left"),
text("Right", id = "right")
),
footer()
),
bindings = list(binding("q", "quit_app", "Quit", priority = TRUE)),
on_action = function(event, state) {
if (event$value == "quit_app") return(quit())
state
}
)Every widget and container accepts optional id and
classes parameters. The id is used for event
handling and updates; classes are used for CSS styling.
Layout containers
grid() – CSS grid layout
Arrange children in a grid with specified rows and columns:
You can set rows, cols, or both. Use CSS
for fine-grained control over column/row sizes:
center() and middle() – Alignment
center() centres children horizontally;
middle() centres vertically. Combine them for perfect
centring:
box() – Bordered container
Wraps a single child with an optional border and title:
Border styles: "none", "round",
"heavy", "double".
collapsible() – Expandable section
A section that can be collapsed/expanded by clicking:
collapsible(
"Advanced Settings",
input(placeholder = "API endpoint", id = "endpoint"),
checkbox("Enable debug mode", id = "debug"),
collapsed = TRUE,
id = "advanced"
)Display widgets
static() – Rich text
Like text() but supports Rich markup
for colours, bold, italic, etc.:
markdown() – Rendered Markdown
Renders Markdown content with headings, lists, code blocks, and more:
digits() – Large number display
Shows text in a large, blocky font – ideal for counters, clocks, and dashboards:
digits("12:34", id = "clock")
digits("0", id = "counter")
# Update the display
update(state$app, "clock", value = "12:35")Supports digits 0-9, colons, spaces, and periods.
pretty_table() – Rich-formatted table
Renders a data.frame as a formatted table (non-interactive):
pretty_table(
head(mtcars, 5),
title = "Motor Trend Cars",
id = "info_table"
)progress_bar() – Progress indicator
progress_bar(total = 100, progress = 0, id = "pb")
# Update progress from a handler
update(state$app, "pb", progress = 42)Options: show_eta (estimated time),
show_percentage.
placeholder() – Placeholder area
A labelled placeholder useful during development:
placeholder("Chart goes here", id = "ph")Input widgets
button() – Clickable button
button("Click Me", id = "btn", tooltip = "Does something cool")
# Handle clicks
on_click = list(
btn = function(event, state) {
notify(state$app, "Button clicked!")
state
}
)Update the label or disable it at runtime:
input() – Single-line text input
input(
placeholder = "Enter your name...",
value = "",
id = "name_input",
tooltip = "Type here",
validators = "number" # optional validation
)Validators: "number", "integer",
"url", or "regex:PATTERN".
Handle value changes and submission (Enter key):
text_area() – Multi-line editor
text_area(
value = "Edit me...",
language = "r", # syntax highlighting
show_line_numbers = TRUE,
id = "editor"
)The language parameter enables syntax highlighting.
Supported languages include "r", "python",
"javascript", "markdown", "json",
"sql", and many more.
masked_input() – Formatted input
Input with a fixed template for structured data:
masked_input(template = "999-999-9999", id = "phone")
masked_input(template = "AA99 9AA", id = "postcode")Template characters: A (letter), 9 (digit),
! (force uppercase), > (force uppercase
following), < (force lowercase following). All other
characters are literal separators.
switch_input() – Toggle switch
switch_input(value = FALSE, id = "dark_mode")radio_set() and radio_button() – Radio group
radio_set(
radio_button("Option A"),
radio_button("Option B"),
radio_button("Option C", value = TRUE), # pre-selected
id = "choice"
)data_table() – Interactive data table
A full-featured interactive table with sorting and row selection:
data_table(
mtcars,
id = "cars_table",
cursor = "row", # "cell", "row", "column", "none"
zebra_stripes = TRUE,
sortable = TRUE
)Update at runtime – add rows, clear data:
option_list() – Selectable list
A scrollable list of options. Fires change events on selection:
option_list(
items = c("Apple", "Banana", "Cherry", "Date"),
id = "fruits"
)
# Update items dynamically
update(state$app, "fruits", items = c("Fig", "Grape", "Kiwi"))selection_list() – Multi-select list
Like option_list() but allows multiple selections:
selection_list(
items = c("Read", "Write", "Execute"),
id = "permissions"
)Navigation widgets
directory_tree() – File system browser
Browse a real directory:
directory_tree(path = ".", id = "files")Fires click events with the selected file path:
content_switcher() – Show one child at a time
content_switcher(
text("Page 1 content", id = "page1"),
text("Page 2 content", id = "page2"),
initial = "page1",
id = "switcher"
)Common widget properties
Most widgets support these runtime updates:
# Show / hide a widget
update(state$app, "widget_id", display = TRUE)
update(state$app, "widget_id", display = FALSE)
# Enable / disable a widget
update(state$app, "widget_id", disabled = TRUE)
update(state$app, "widget_id", disabled = FALSE)
# Change text content (text, static, markdown)
update(state$app, "widget_id", content = "New text")
# Change value (input, digits, switch)
update(state$app, "widget_id", value = "new value")
# Change button/checkbox label
update(state$app, "widget_id", label = "New Label")CSS styling
rtui supports Textual’s CSS dialect for fine-grained styling. Pass a
CSS string to the css parameter:
quick_app(
layout = vstack(
text("Styled!", id = "styled_text"),
id = "root"
),
css = "
#styled_text {
color: cyan;
text-style: bold;
text-align: center;
padding: 2;
border: heavy green;
}
#root {
align: center middle;
}
"
)Common CSS properties:
-
background,color– colours -
width,height– sizes (auto,1fr,50%,30, etc.) -
padding,margin– spacing -
border–none,tall,heavy,round,double -
text-align–left,center,right -
text-style–bold,italic,underline,reverse -
display–block,none -
dock–top,bottom,left,right -
align–left top,center middle,right bottom
Combine themes with your own CSS by concatenating:
css = paste0(
tui_theme("dracula"),
"
#sidebar { width: 30; border-right: tall $accent; }
#main { width: 1fr; padding: 1; }
"
)The $accent, $surface, $text,
and $text-muted variables from Textual are available in
your CSS.
Example: Dashboard layout
Here’s a complete dashboard layout combining multiple concepts:
library(rtui)
quick_app(
title = "Dashboard",
layout = vstack(
header(),
hstack(
# Sidebar
vstack(
static("[bold]Navigation[/bold]"),
rule(),
option_list(
items = c("Overview", "Reports", "Settings"),
id = "nav"
),
id = "sidebar"
),
# Main content
vstack(
static("[bold]Welcome to the Dashboard[/bold]", id = "title"),
rule(),
hstack(
box(digits("42", id = "metric1"), border = "round", title = "Users"),
box(digits("128", id = "metric2"), border = "round", title = "Sales"),
box(digits("99%", id = "metric3"), border = "round", title = "Uptime")
),
text_plot(id = "chart"),
id = "main"
),
id = "content"
),
footer()
),
on_mount = function(event, state) {
plot_bar(state$app, "chart",
labels = c("Mon", "Tue", "Wed", "Thu", "Fri"),
values = c(120, 200, 150, 180, 220),
title = "Weekly Activity",
color = "cyan")
state
},
bindings = list(
binding("q", "quit_app", "Quit", priority = TRUE)
),
on_action = function(event, state) {
if (event$value == "quit_app") return(quit())
state
},
css = paste0(
tui_theme("catppuccin"),
"
#sidebar { width: 25; padding: 1; border-right: tall $accent; }
#main { width: 1fr; padding: 1; }
#chart { height: 1fr; }
"
)
)