Skip to contents

Shiny applications have become a very popular and powerful way of creating interactive we applications that leverage the power of R. There are numerous ways Shiny apps can be deployed including ShinyApps.io or hosted services such as DigitalOcean or Amazon AWS. The Golem project provides a framework for developing Shiny apps as R packages. However, with Golem, the Shiny app is the primary product. This package provides a framework for including Shiny apps where the app is designed to demonstrate the features of the package. That is, it is more like the role R package demos had before they were obfuscated by package vignettes and tests (via the usethis package. The primary function used by package users is shiny_demo. Much like the built in demo() function, calling shiny_demo() without any parameters will list available Shiny demo apps in all the loaded packages. Calling shiny_demo(topic) will launch the Shiny app. In addition to providing a standardized structure for including Shiny demo apps within your package, it also provides functionality to allow the package user to pass parameters to the Shiny app.

For the package developer, Shiny demo apps need to be placed in the inst/ folder. The ShinyDemo::shiny_demo() function will automatically find apps located there. The files placed in this file are the same as building a standalone Shiny app (i.e. you can use the globa.R, shiny.R, and ui.R files or the single app.R file). Supporting files should also be placed here (e.g. www/, R/, etc.). Consider the following Shiny app that displays information about data frames.

The ui.R file:

fluidPage(
    titlePanel("Data Frame Viewer"),
    uiOutput('df_select'),
    tabsetPanel(
        tabPanel(
            'Structure',
            verbatimTextOutput('df_structure')
        ),
        tabPanel(
            'Table',
            dataTableOutput('df_table')
        )
    )
)

The server.R file:

function(input, output, session) {
    get_data <- reactive({
        req(input$dataframe)
        df <- NULL
        if(exists(input$dataframe)) {
            df <- get(input$dataframe)
        }
        return(df)
    })
    
    output$df_select <- renderUI({
        ls_out <- ShinyDemo::ls_all()
        dfs <- character()
        for(i in ls_out) {
            if(is.data.frame(get(i))) {
                dfs <- c(dfs, i)
            }
        }
        if(length(dfs) > 0) {
            selectInput('dataframe', 'Select Data Frame', choices = dfs)
        } else {
            p('No data.frames found!')
        }
    })
    
    output$df_structure <- renderPrint({
        df <- get_data()
        str(df)
    })
    
    output$df_table <- renderDataTable({
        df <- get_data()
        return(df)
    })
}

This application will run as is but very likely will not list any data frames to view (assuming run from a fresh R session). If you want to provide data frame to view to the end user, you could load the data frame in global.R, for example. The ls_all() function work like ls(), except it will traverse the environment chain up to .GlobalEnv. This Shiny app is included in the ShinyDemo package and can be launched using the follow command:

shiny_demo('df_viewer', 'ShinyDemo')

Passing parameters to Shiny applications

A is this application is not very useful. However, we can modify the server function to use the ShinyDemo::get_shiny_parameters() function in order to find objects that can be passed to the application. As shown below, we need to have a list object called data_frames that contains data frames the application will display. The type_check parameter is optional, but is called if the object is found to determine if it of the correct type.

df_viewer_server <- function(input, output, session) {
    get_data_frames <- reactive({
        get_shiny_parameter(param = 'data_frames', type_check = is.list)
    })
    
    get_data <- shiny::reactive({
        shiny::req(input$dataframe)
        data_frames <- get_data_frames()
        df <- NULL
        if(input$dataframe %in% names(data_frames)) {
            df <- data_frames[[input$dataframe]]
        }
        return(df)
    })
    
    output$df_select <- shiny::renderUI({
        data_frames <- get_data_frames()
        shiny::selectInput('dataframe', 'Select Data Frame', choices = names(data_frames))
    })
    
    output$df_structure <- shiny::renderPrint({
        df <- get_data()
        str(df)
    })
    
    output$df_table <- DT::renderDT({
        df <- get_data()
        return(df)
    })
}

The ShinyDemo::runAppWithParams is essentially a wrapper to shiny::runApp() but allows for passing arbitrary parameters to the Shiny application. Note that any shiny::runApp() or shiny::shinyApp() parmaeters can be specified here (we set the port for example).

runAppWithParams(
    ui = ShinyDemo::df_viewer_ui,
    server = ShinyDemo::df_viewer_server,
    data_frames = list(mtcars = mtcars, faithful = faithful),
    port = 2112)

If you wish to run your Shiny application from an app.R script as well as being in the package, there is slight modification that you need to make. When you define your UI and server code with app.R (or ui.R and server.R), they are defined in the global environment. When we place these functions inside a package they will be run from within the package environment. Therefore anything we define out side the functions (or in global.R) will not be accessible. However, we can modify the environment the functions will run as shown below.

library(shiny)
library(ShinyDemo)

data("mtcars")
data("faithful")

data_frames <- list(mtcars = mtcars,
                    faithful = faithful)

ui <- ShinyDemo::df_viewer_ui
environment(ui) <- environment()
server <- ShinyDemo::df_viewer_server
environment(server) <- environment()

shinyApp(ui = ui, server = server)

Utility Functions

The ShinyDemo has a few utility functions.

ls_all()

The ls_all() function works like ls() in that it returns a character vector listing the names of objects available, however ls_all() will traverse the environment chain until it reaches the .GlobalEnv.

renderRmd

This will render an R markdown file from a Shiny application. If the input parameter is provided it will be available within the R markdown file at render time.

includeVignette

This works like includeMarkdown except it will include an HTML vignette within the Shiny app.

renderVignette

Similar to includeVignette except the source R markdown for the vignette will be rendered instead of using the prebuilt vignette from the package directory. This avoids some potential formatting issues of includeVignette since it will render the document as a partial HTML file (i.e. the <head></head> content will be excluded).