Shiny Dashboards
bslib

Colin Rundel

The bslib R package provides a modern UI toolkit for Shiny and R Markdown based on Bootstrap. It facilitates:

  • Custom theming of Shiny apps and R Markdown documents.
    • Apps can even be themed interactively in real-time.
  • Use of modern versions of Bootstrap and Bootswatch
    • Shiny and R Markdown currently default to Bootstrap 3 and may continue to do so to maintain backwards compatibility.
  • Creation of delightful and customizable Shiny dashboards
    • The underlying UI components (e.g., cards, value boxes, sidebars, etc) are also designed to work in other contexts (e.g., in R Markdown).

Demo 10 - bslib dashboard

demos/demo10.R

library(tidyverse)
library(shiny)
library(bslib)

ggplot2::theme_set(ggplot2::theme_bw())

d = readr::read_csv(here::here("data/weather.csv"))

d_vars = d |>
  select(where(is.numeric)) |>
  names()

shinyApp(
  ui = page_sidebar(
    title = "bslib dashboard",
    sidebar = sidebar(
      selectInput(
        "city", "Select a city",
        choices = c("Chicago", "Durham", "Sedona", "New York", "Los Angeles"),
        selected = "Chicago", multiple = TRUE
      ),
      selectInput(
        "var", "Select a variable",
        choices = d_vars, selected = "humidity"
      )
    ),
    card(
      card_header("Temperature"), 
      plotOutput("plot_temp")
    ),
    card(
      card_header(
        textOutput("header_other")
      ),
      plotOutput("plot_other")
    )
  ),
  server = function(input, output, session) {
    d_city = reactive({
      d |>
        filter(city %in% input$city)
    })
    
    output$plot_temp = renderPlot({
      d_city() |>
        ggplot(aes(x=time, y=temp, color=city)) +
        geom_line()
    })
    
    output$header_other = renderText({input$var})
    
    output$plot_other = renderPlot({
      d_city() |>
        ggplot(aes(x=time, y=.data[[input$var]], color=city)) +
        geom_line()
    })
  }
)

Layouts

As we’ve just seen the basic layout of elements is to place each element on its own row (e.g. each card in Demo 11).

Columns can be constructed using layout_columns() (or layout_columns_wrap()) where each element is a column

  • Columns will have equal width by default

  • col_widths can be used to specify width (in terms of bootstrap columns)

  • Total widths >12 will result in multiple rows

  • Negative widths result in empty columns

  • layout_columns() can be nested inside other layout_columns() for mixed layouts

Demo 11 - layout

demos/demo11.R

library(tidyverse)
library(shiny)
library(bslib)

ggplot2::theme_set(ggplot2::theme_bw())

d = readr::read_csv(here::here("data/weather.csv"))

d_vars = d |>
  select(where(is.numeric)) |>
  names()


shinyApp(
  ui = page_sidebar(
    title = "bslib dashboard",
    sidebar = sidebar(
      selectInput(
        "city", "Select a city",
        choices = c("Chicago", "Durham", "Sedona", "New York", "Los Angeles"),
        selected = "Chicago", multiple = TRUE
      ),
      selectInput(
        "var", "Select a variable",
        choices = d_vars, selected = "humidity"
      )
    ),
    layout_columns(
      col_widths = c(10,2,-1,10,-1),
      card(
        card_header("Temperature"), 
        plotOutput("plot_temp")
      ),
      list(
        value_box(
          title = "Min temp",
          value = textOutput("min_temp"),
          showcase = bsicons::bs_icon("thermometer-low"),
          theme_color = "primary"
        ),
        value_box(
          title = "Max temp",
          value = textOutput("max_temp"),
          showcase = bsicons::bs_icon("thermometer-high"),
          theme_color = "danger"
        )
      ),
      card(
        card_header(
          textOutput("header_other")
        ),
        plotOutput("plot_other")
      )
    )
  ),
  server = function(input, output, session) {
    d_city = reactive({
      d |>
        filter(city %in% input$city)
    })
    
    output$plot_temp = renderPlot({
      d_city() |>
        ggplot(aes(x=time, y=temp, color=city)) +
        geom_line()
    })
    
    output$min_temp = renderText({min(d_city()$temp)})
    output$max_temp = renderText({max(d_city()$temp)})
    
    output$header_other = renderText({input$var})
    
    output$plot_other = renderPlot({
      d_city() |>
        ggplot(aes(x=time, y=.data[[input$var]], color=city)) +
        geom_line()
    })
  }
)

Demo 12 - other components

demos/demo12.R

library(tidyverse)
library(shiny)
library(bslib)

ggplot2::theme_set(ggplot2::theme_bw())

d = readr::read_csv(here::here("data/weather.csv"))

d_vars = d |>
  select(where(is.numeric)) |>
  names()

shinyApp(
  ui = page_sidebar(
    title = "bslib dashboard",
    sidebar = sidebar(open=FALSE),
    card(
      card_header(
        "Temperature",
        popover(
          bsicons::bs_icon("gear"),
          title = "Input controls",
          selectInput(
            "city", "Select a city",
            choices = c("Chicago", "Durham", "Sedona", "New York", "Los Angeles"),
            selected = "Chicago", multiple = TRUE
          )
        )
      ), 
      plotOutput("plot_temp")
    ),
    card(
      card_header(textOutput("header_other")),
      layout_sidebar(
        sidebar = sidebar(
          position = "right", open = FALSE,
          selectInput(
            "var", "Select a variable",
            choices = d_vars, selected = "humidity"
          )
        ),
        plotOutput("plot_other")
      )
    )
  ),
  server = function(input, output, session) {
    d_city = reactive({
      d |>
        filter(city %in% input$city)
    })
    
    output$plot_temp = renderPlot({
      d_city() |>
        ggplot(aes(x=time, y=temp, color=city)) +
        geom_line()
    })
    
    output$header_other = renderText({input$var})
    
    output$plot_other = renderPlot({
      d_city() |>
        ggplot(aes(x=time, y=.data[[input$var]], color=city)) +
        geom_line()
    })
  }
)

Your Turn - Faithful Makeover

Create a new Shiny app using RStudio’s Create Shiny App template. You can call the app exercises/makeover.

Your task is to re-design the starting shiny template app using bslib. Here are some functions to try:

  • page_*()
  • layout_sidebar()
  • layout_columns()
  • card()
  • value_box()
08:00