R Shiny - Part III

Lokesh Mano

NBIS, SciLifeLab

14-Apr-2025

Contents

Modules

  • Parts of an app can be modularized and reused like mini apps
  • They can be used where needed like functions
  • Modules have their own namespace
  • A module has a UI part and a server part
  • Define namespace using NS()
  • Modules can be nested

Modules

#| '!! shinylive warning !!': |
#|   shinylive does not work in self-contained HTML documents.
#|   Please set `embed-resources: false` in your metadata.
#| standalone: true
#| components: [editor, viewer]
counter_ui <- function(id) {
  ns <- NS(id)
  div(
    actionButton(ns("btn"), label = "Counter"),
    textOutput(ns("txt"))
  )
}

counter_server <- function(id) {
  moduleServer(id, function(input, output, session) {
    count <- reactiveVal(0)
    output$txt <- renderText({
      count(count() + 1)
      paste0("Counter ", id, ":", count())
    }) |> bindEvent(input$btn)
  })
}

shinyApp(
  ui = fluidPage(
    counter_ui(id = "1"),
    counter_ui(id = "2")
  ),

  server = function(input, output, session) {
    counter_server("1")
    counter_server("2")
  }
)

Debugging & Optimizing

  • print() statements

  • Interrupt execution and inspect environment browser()

  • Visualize relationships using reactlog

Debugging & Optimizing

Code execution

App launch

ui <- fluidPage(textInput("text_input",label="textInput"), textOutput("text_output"))
server <- function(input,output) {
  output$text_output <- renderText({input$text_input})
}

New user

ui <- fluidPage(textInput("text_input",label="textInput"), textOutput("text_output"))
server <- function(input,output) {
  output$text_output <- renderText({input$text_input})
}

Widget update

ui <- fluidPage(textInput("text_input",label="textInput"), textOutput("text_output"))
server <- function(input,output) {
  output$text_output <- renderText({input$text_input})
}

Theming

  • Use shinythemes
library(shinythemes)
fluidPage(theme = shinytheme("cerulean"))
  • Live theme selector
library(shinythemes)
fluidPage(theme = shinythemes::themeSelector())

shinythemes

#| '!! shinylive warning !!': |
#|   shinylive does not work in self-contained HTML documents.
#|   Please set `embed-resources: false` in your metadata.
#| standalone: true
#| viewerHeight: 650
webr::install("shinythemes")

shinyApp(
  ui = fluidPage(
    shinythemes::themeSelector(),
    sidebarPanel(
      textInput("txt", "Text input:", "text here"),
      sliderInput("slider", "Slider input:", 1, 100, 30),
      actionButton("action", "Button"),
      actionButton("action2", "Button2", class = "btn-primary")
    ),
    mainPanel(
      tabsetPanel(
        tabPanel("Tab 1"),
        tabPanel("Tab 2")
      )
    )
  ),
  server = function(input, output) {}
)

Theming

  • Use bslib
library(bslib)
ui <- page_fluid(
  theme = bs_theme(version = 5)
)
  • Live theme selector
library(bslib)
bs_theme_preview()

bslib, Bootswatch

Theming using custom CSS

  • Insert in the head
ui <- fluidPage(
  tags$head(
    tags$style(HTML("
      @import url('https://fonts.googleapis.com/css2?family=Yusei+Magic&display=swap');
      h2 {
        font-family: 'Yusei Magic', sans-serif;
      }"))
  ))
  • Add styles directly
ui <- fluidPage(h2("Old Faithful Geyser Data", style = "font-size: 2em;"))
  • Load custom CSS file placed in www/
ui <- fluidPage(
  tags$head(tags$link(rel = "stylesheet", type = "text/css", href = "styles.css"))
)

CSS styling

Deployment

  • R scripts
    • GitHub
    • R Package
  • Container (Docker …)
  • Hosting
    • Shinyapps.io
    • Shiny Server (Free)
    • Shiny Server Pro
  • Automatically deploying to shinyapps.io

Shinyapps.io

library(rsconnect)
rsconnect::setAccountInfo(name="username", token="HDFGT46YF7TDT6474G47", secret="hdgTDF5FStgdkpJ")
deployApp(appName="name")

Interactive documents • Quarto

---
title: "Interactive scatterplot"
format: html
server: shiny
---

```{r}
library(shiny)
library(ggplot2)

selectInput("x_var", "X-axis Variable:", choices=names(mtcars), selected = "hp"),
selectInput("y_var", "Y-axis Variable:", choices=names(mtcars), selected = "mpg"),
plotOutput("plot")
```

```{r}
#| context: server
output$plot <- renderPlot({
  ggplot(mtcars, aes_string(x = input$x_var, y = input$y_var)) +
    geom_point() +
    labs(title = "Interactive mtcars scatterplot", 
         x = input$x_var, 
         y = input$y_var)
})
```

Interactive documents • Rmarkdown

  • Shiny can run in RMarkdown documents. Set YAML runtime: shiny.
---
title: Interactive document
output: html_document
runtime: shiny
---
  • Shiny widgets can be included directly
```{r}
selectInput('n_breaks',label='Number of bins:',choices=c(10,20,35,50),selected=20)
```

Interactive documents

  • Whole shiny apps can be included directly
```{r}
shinyApp(
  ui=fluidPage(),
  server=function(input,output) {}
)
```
  • Hosted shiny apps can be embedded using <iframe>
<iframe src="https://user.shinyapps.io/app"></iframe>

Other topics

Extensions

Learning & community

Thank you. Questions?

Slide inspirations: Roy Francis (NBIS, RaukR2024)