| Title: | Build Consistent 'shiny' App Layouts with Less Code |
|---|---|
| Description: | Short, consistent wrappers around 'shiny', 'bslib' and 'shinyjs' for building multi-page 'shiny' apps. One function call builds the page with a navigation bar, logo, title and dark mode switch. Tab functions nest pages up to three levels deep, with optional sidebars. Input functions create full-width inputs with optional information tooltips, and helpers switch buttons on and off and show or hide tabs from the server. Includes a starter app template and an example app that explores the Titanic passenger data. |
| Authors: | Daniella DeWeerd [aut, cre, cph], Van Andel Institute [cph, fnd] |
| Maintainer: | Daniella DeWeerd <[email protected]> |
| License: | GPL-3 |
| Version: | 0.1.0 |
| Built: | 2026-10-11 10:49:37 UTC |
| Source: | https://github.com/vari-bbc/ezrshiny |
Puts an info icon after the label. Hovering over the label or the icon shows
tooltipText. Every nav*() input and output function calls this for you
through its tooltipText argument. Call it directly only when building your
own inputs.
addTooltip(label, tooltipText)addTooltip(label, tooltipText)
label |
The label: a string or a 'shiny' tag. |
tooltipText |
Text to show on hover. |
The label, with a tooltip if one was given.
addTooltip("Upload data", "CSV files only") addTooltip("Upload data", NA)addTooltip("Upload data", "CSV files only") addTooltip("Upload data", NA)
Creates a folder holding a ready-to-edit app laid out the standard way:
createEZApp(path, appName = "My App", overwrite = FALSE)createEZApp(path, appName = "My App", overwrite = FALSE)
path |
Folder to create the app in. It is created if needed. |
appName |
Name shown in the app's navigation bar. |
overwrite |
|
app.R: the app, with numbered sections for loading packages, the UI and
the server, and comments explaining what goes where.
Functions/: put your helper .R files here. app.R loads them all
with sourceFunctions().
www/: put images such as logos here. Give just the file name to
UINav(logoFile = ...).
Necessary_Files/: put files the app needs at start, such as a data
template users can download.
Open app.R and run it with shiny::runApp("<path>") or the Run App button.
The path to the new app.R, invisibly.
appFolder <- file.path(tempdir(), "myFirstApp") createEZApp(appFolder, appName = "My First App") list.files(appFolder) if (interactive()) { shiny::runApp(appFolder) }appFolder <- file.path(tempdir(), "myFirstApp") createEZApp(appFolder, appName = "My First App") list.files(appFolder) if (interactive()) { shiny::runApp(appFolder) }
Greys out inputs so they can't be used, or makes them usable again. Use this in the server to stop users clicking a button or download before the app is ready for it, for example deactivating every download at start and activating each one once there is something to download.
deactivateItems(itemIDs) activateItems(itemIDs)deactivateItems(itemIDs) activateItems(itemIDs)
itemIDs |
IDs of the inputs to switch, as a character vector. |
Needs shinyjs::useShinyjs() in the UI, which UINav() adds for you.
NULL, invisibly. Called for its effect on the page.
if (interactive()) { shiny::shinyApp( ui = UINav( appName = "Demo", singleTab("Home", navCheckbox("ready", "Ready to download?"), navDownload("theDownload", "Download") ) ), server = function(input, output, session) { shiny::observe({ if (isTRUE(input$ready)) { activateItems("theDownload") } else { deactivateItems("theDownload") } }) } ) }if (interactive()) { shiny::shinyApp( ui = UINav( appName = "Demo", singleTab("Home", navCheckbox("ready", "Ready to download?"), navDownload("theDownload", "Download") ) ), server = function(input, output, session) { shiny::observe({ if (isTRUE(input$ready)) { activateItems("theDownload") } else { deactivateItems("theDownload") } }) } ) }
Opens "Titanic Explorer", a complete example app built with EZRShiny that explores who survived the Titanic, using R's built-in datasets::Titanic data. It uses every kind of tab (a single page, a page with a sidebar, a row of sub tabs and a drop-down menu of tabs), hides tabs until they are useful, switches buttons on and off, and follows the standard folder layout:
runExampleApp(...)runExampleApp(...)
... |
Passed to |
app.R: packages, UI and server.
Functions/: helper functions, loaded with sourceFunctions().
Necessary_Files/: the Titanic data as a CSV, one row per person.
www/: the logo shown in the navigation bar.
Its code is a good starting point for your own app. Find it with
system.file("examples", "titanicExplorer", package = "EZRShiny").
Needs the 'ggplot2' package.
Does not return while the app is running.
# Where the example app's files are: list.files(system.file("examples", "titanicExplorer", package = "EZRShiny"), recursive = TRUE) if (interactive()) { runExampleApp() }# Where the example app's files are: list.files(system.file("examples", "titanicExplorer", package = "EZRShiny"), recursive = TRUE) if (interactive()) { runExampleApp() }
Loads all of an app's helper functions in one call, so app.R doesn't need
a source() line per file. Every file ending in .R in the folder is
sourced, in alphabetical order. Subfolders are not searched.
sourceFunctions(functionFolderPath, envir = parent.frame())sourceFunctions(functionFolderPath, envir = parent.frame())
functionFolderPath |
Path to the folder holding the |
envir |
Environment to load the functions into. Defaults to the one
|
A relative path is looked up from the working directory first. If it isn't
found there, it is looked up from the project root with here::here().
The paths of the sourced files, invisibly.
folder <- file.path(tempdir(), "Functions") dir.create(folder, showWarnings = FALSE) writeLines("addOne <- function(x) x + 1", file.path(folder, "addOne.R")) sourceFunctions(folder) addOne(1)folder <- file.path(tempdir(), "Functions") dir.create(folder, showWarnings = FALSE) writeLines("addOne <- function(x) x + 1", file.path(folder, "addOne.R")) sourceFunctions(folder) addOne(1)
Writes a short marker to the console so the app's log shows which part of
the server is running. Put startSection() at the top of an
observeEvent() and endSection() at the bottom, using the same name.
Markers are sent as messages, so suppressMessages() hides them.
startSection(sectionName) endSection(sectionName)startSection(sectionName) endSection(sectionName)
sectionName |
Name of the section, shown in the log. |
NULL, invisibly. Called for the message it writes.
startSection("Read in data") endSection("Read in data")startSection("Read in data") endSection("Read in data")
Tabs that go inside a biLevelTab() or a triLevelTab():
triSubTab(title, ..., icon = NULL, id = title, height = cardHeight()) subTab(title, ..., value = title) subSidebarTab(title, sidebarElements = NULL, ..., value = title, icon = NULL) triSubSidebarTab( title, sidebarElements = NULL, ..., icon = NULL, id = title, height = cardHeight() ) subTwoColPage(leftSide, rightSide)triSubTab(title, ..., icon = NULL, id = title, height = cardHeight()) subTab(title, ..., value = title) subSidebarTab(title, sidebarElements = NULL, ..., value = title, icon = NULL) triSubSidebarTab( title, sidebarElements = NULL, ..., icon = NULL, id = title, height = cardHeight() ) subTwoColPage(leftSide, rightSide)
title |
Tab name shown in the tab strip or menu. |
... |
Contents. See the table above. |
icon |
Optional icon shown before the title. |
id |
ID of the row of sub tabs inside the tab. Defaults to the title with spaces removed. |
height |
Height of the card holding the tab's contents. |
value |
ID of the tab, used with |
sidebarElements |
A |
leftSide, rightSide
|
Contents of the left and right columns. Wrap
several items in |
| Function | Goes inside | Holds |
subTab() |
biLevelTab(), triSubTab(), triSubSidebarTab() |
inputs and outputs |
subSidebarTab() |
biLevelTab(), triSubTab(), triSubSidebarTab() |
a sidebar plus inputs and outputs |
triSubTab() |
triLevelTab() |
subTab(), subSidebarTab() |
triSubSidebarTab() |
triLevelTab() |
a sidebar plus subTab(), subSidebarTab()
|
subTwoColPage() isn't a tab: it splits a page into two equal columns and
can go inside any tab.
A 'bslib' navigation panel, or for subTwoColPage() a column layout.
biLevelTab("Explore", subSidebarTab("PCA", sidebarElements = list(navButton("runPCA", "Create PCA")), navOutputPlotly("pcaPlot") ), subTab("Table", subTwoColPage(navOutputTable("tableLeft"), navOutputTable("tableRight")) ) )biLevelTab("Explore", subSidebarTab("PCA", sidebarElements = list(navButton("runPCA", "Create PCA")), navOutputPlotly("pcaPlot") ), subTab("Table", subTwoColPage(navOutputTable("tableLeft"), navOutputTable("tableRight")) ) )
Tabs that go directly inside UINav(). Pick one by how much the tab holds:
triLevelTab(title, ..., icon = NULL) biLevelTab(title, ..., icon = NULL, id = title, height = cardHeight()) sidebarLevelTab( title, sidebarElements = NULL, ..., icon = NULL, height = cardHeight() ) singleTab(title, ..., icon = NULL, height = cardHeight())triLevelTab(title, ..., icon = NULL) biLevelTab(title, ..., icon = NULL, id = title, height = cardHeight()) sidebarLevelTab( title, sidebarElements = NULL, ..., icon = NULL, height = cardHeight() ) singleTab(title, ..., icon = NULL, height = cardHeight())
title |
Tab name shown in the navigation bar. |
... |
Contents. See the table above. |
icon |
Optional icon shown before the title, for example
|
id |
ID of the row of sub tabs, used with |
height |
Height of the card holding the tab's contents. Change the
default for every tab with |
sidebarElements |
A |
| Function | Holds | Put inside it |
singleTab() |
one page | inputs and outputs |
sidebarLevelTab() |
one page with a sidebar | inputs and outputs |
biLevelTab() |
a row of sub tabs | subTab(), subSidebarTab() |
triLevelTab() |
a drop-down menu of tabs with sub tabs | triSubTab(), triSubSidebarTab()
|
A 'bslib' navigation panel or menu.
singleTab("About", shiny::p("This app explores the Titanic data.")) sidebarLevelTab("Plot", sidebarElements = list(navButton("makePlot", "Make plot")), navOutputPlot("thePlot") ) biLevelTab("Explore", subTab("Summary", navOutputTable("summaryTable")), subTab("Plots", navOutputPlot("summaryPlot")) ) triLevelTab("Analyses", triSubTab("Group A", subTab("Table", navOutputTable("tableA")) ), triSubTab("Group B", subTab("Table", navOutputTable("tableB")) ) )singleTab("About", shiny::p("This app explores the Titanic data.")) sidebarLevelTab("Plot", sidebarElements = list(navButton("makePlot", "Make plot")), navOutputPlot("thePlot") ) biLevelTab("Explore", subTab("Summary", navOutputTable("summaryTable")), subTab("Plots", navOutputPlot("summaryPlot")) ) triLevelTab("Analyses", triSubTab("Group A", subTab("Table", navOutputTable("tableA")) ), triSubTab("Group B", subTab("Table", navOutputTable("tableB")) ) )