One rule
Call useGlassTabs() once somewhere in your UI before
using any widget.
ui <- fluidPage(
useGlassTabs(),
# widgets go here
)Tabs: basic pattern
ui <- fluidPage(
useGlassTabs(),
glassTabsUI(
"main",
glassTabPanel("overview", "Overview", selected = TRUE, h3("Overview")),
glassTabPanel("details", "Details", h3("Details"))
)
)
server <- function(input, output, session) {
active_tab <- glassTabsServer("main")
}Tabs: inside a Shiny module
The key rule: use ns() in the UI, use the
bare id in the server.
# UI function
my_ui <- function(id) {
ns <- NS(id)
tagList(
useGlassTabs(),
glassTabsUI(
ns("tabs"), # ns() wraps the id in the UI
glassTabPanel("summary", "Summary", selected = TRUE, h3("Summary")),
glassTabPanel("detail", "Detail", h3("Detail"))
)
)
}
# Server function
my_server <- function(id) {
moduleServer(id, function(input, output, session) {
active <- glassTabsServer("tabs") # bare id — no ns() here
observeEvent(active(), {
message("Active tab: ", active())
})
})
}
# App
ui <- fluidPage(my_ui("explorer"))
server <- function(input, output, session) my_server("explorer")
if (interactive()) shinyApp(ui, server)Tabs: dynamic values and selected
# Build panels from data
tab_defs <- list(
list(value = "revenue", label = "Revenue"),
list(value = "orders", label = "Orders"),
list(value = "returns", label = "Returns")
)
panels <- lapply(tab_defs, function(t) {
glassTabPanel(t$value, t$label, h3(t$label))
})
ui <- fluidPage(
useGlassTabs(),
do.call(glassTabsUI, c(list("metrics", selected = "orders"), panels))
)
server <- function(input, output, session) {
active <- glassTabsServer("metrics")
}Note: tab
valuestrings must be unique —glassTabsUI()will error on duplicates. Values are plain strings; they do not need to match labels.
Tabs: server actions
# Switch active tab
updateGlassTabsUI(session, "main", "details")
# Hide or show a tab
hideGlassTab(session, "main", "admin")
showGlassTab(session, "main", "admin")
# Append or remove a tab at runtime
appendGlassTab(
session, "main",
glassTabPanel("compare", "Compare", h3("Compare")),
select = TRUE
)
removeGlassTab(session, "main", "compare")Tabs: common options
glassTabsUI(
"main",
glassTabPanel("a", "A", selected = TRUE, p("A")),
glassTabPanel("b", "B", p("B")),
selected = "a",
wrap = TRUE,
compact = FALSE, # set TRUE inside dashboard cards for tighter layout
extra_ui = tags$div("Right side UI"),
theme = "light"
)Tabs: compact mode (dashboard cards)
Use compact = TRUE when embedding glasstabs inside a
tight layout such as a bs4Dash::bs4Card(). It reduces
margins, padding, and font size so the widget does not overflow the
card.
bs4Card(
title = "Explorer", width = 12,
glassTabsUI(
"tabs",
glassTabPanel("a", "A", selected = TRUE, p("Content")),
glassTabPanel("b", "B", p("More content")),
compact = TRUE # reduced spacing for card context
)
)Multi-select: basic pattern
choices <- c(Revenue = "revenue", Orders = "orders", Returns = "returns")
ui <- fluidPage(
useGlassTabs(),
glassMultiSelect("metric", choices),
glassFilterTags("metric")
)
server <- function(input, output, session) {
metric <- glassMultiSelectValue(input, "metric")
}Multi-select: update from server
updateGlassMultiSelect(
session,
"metric",
selected = c("revenue", "orders"),
check_style = "filled"
)
# Clear selection
updateGlassMultiSelect(session, "metric", selected = character(0))Multi-select: useful arguments
glassMultiSelect(
"metric",
choices,
selected = unname(choices),
label = "Metrics",
placeholder = "Choose metrics",
all_label = "All metrics",
check_style = "checkbox",
show_style_switcher = TRUE,
show_select_all = TRUE,
show_clear_all = TRUE,
theme = "dark"
)Single-select: basic pattern
regions <- c("All Regions" = "all", North = "north", South = "south")
ui <- fluidPage(
useGlassTabs(),
glassSelect("region", regions, selected = "all")
)
server <- function(input, output, session) {
region <- glassSelectValue(input, "region")
}Single-select: update from server
updateGlassSelect(session, "region", selected = "south")
# Clear value
updateGlassSelect(session, "region", selected = character(0))Single-select: useful arguments
glassSelect(
"region",
regions,
selected = "all",
label = "Region",
placeholder = "Pick a region",
searchable = TRUE,
clearable = TRUE,
include_all = FALSE,
check_style = "checkbox",
theme = "light"
)Tabs: inside bs4Dash
glassTabsUI() works inside
bs4Dash::tabItem(). Place useGlassTabs() in
the dashboardBody() (or anywhere before the first widget —
once is enough).
library(shiny)
library(bs4Dash)
ui <- bs4DashPage(
header = bs4DashNavbar(),
sidebar = bs4DashSidebar(
bs4SidebarMenu(
bs4SidebarMenuItem("Explorer", tabName = "explorer", icon = icon("table"))
)
),
body = bs4DashBody(
useGlassTabs(), # place once, anywhere in the body
bs4TabItems(
bs4TabItem(
tabName = "explorer",
bs4Card(
title = "Data explorer",
width = 12,
glassTabsUI(
"s3_tabs",
glassTabPanel("buckets", "Buckets", selected = TRUE,
p("List of S3 buckets here.")
),
glassTabPanel("objects", "Objects",
p("Object browser here.")
),
glassTabPanel("preview", "Preview",
p("File preview here.")
)
)
)
)
)
)
)
server <- function(input, output, session) {
active <- glassTabsServer("s3_tabs")
}
if (interactive()) shinyApp(ui, server)
useGlassTabs()only needs to appear once. Inside a module that renders into a bs4Dash layout, call it in your module’s UI function.
Theme helpers
# Tabs
glassTabsUI(
"main",
glassTabPanel("a", "A", selected = TRUE, p("A")),
theme = glass_tab_theme(
halo_bg = "rgba(251,191,36,0.15)",
tab_active_text = "#fef3c7"
)
)
# Select widgets
glassMultiSelect(
"metric", choices,
theme = glass_select_theme(
mode = "dark",
accent_color = "#38bdf8"
)
)Input values at a glance
| Widget | Server value |
|---|---|
glassTabsUI("main", ...) |
input[["main-active_tab"]] or
glassTabsServer("main")()
|
glassMultiSelect("metric", ...) |
input$metric or
glassMultiSelectValue(input, "metric")$selected()
|
| multi-select style |
input$metric_style or
glassMultiSelectValue(input, "metric")$style()
|
glassSelect("region", ...) |
input$region or
glassSelectValue(input, "region")()
|
conditionalPanel integration
Use glassTabCondition() to avoid constructing the input
key manually:
# Instead of: condition = "input['main-active_tab'] === 'details'"
conditionalPanel(
condition = glassTabCondition("main", "details"),
p("Only visible on the Details tab.")
)
# Inside a module — pass the same id as glassTabsUI():
# glassTabsUI(ns("tabs"), ...)
conditionalPanel(
condition = glassTabCondition(NS("mymod")("tabs"), "details"),
p("Only visible on the Details tab.")
)View the changelog
glasstabs_news() # prints changelog to the consoleCommon gotchas
-
useGlassTabs()must be called once in the UI. - In a module, use
ns("tabs")in the UI but pass the bare"tabs"toglassTabsServer()— notns("tabs"). Passing a namespaced id (containing-) will produce a warning with a suggested fix. -
glassTabPanel()values must be unique within aglassTabsUI()call — duplicates cause an error naming the offending value. -
selectedinglassTabsUI()refers to the tab value, not the label. -
glassSelectValue()returns a reactive function, not a list. -
glassMultiSelectValue()returns a list withselected()andstyle(). - For
glassMultiSelect(),selectedshould use choice values, not labels. - Use
compact = TRUEinside bs4Dash cards or any tight layout to reduce widget spacing. - If you add new JS/CSS behavior during development, reinstall or
load_all()before retesting.