Polishing, Deployment, and Moving Forward

Mark Andrews

The gap between working and usable

A working application and a usable application are different things. A working application does what the developer intended. A usable application communicates clearly to someone who was not in the room when it was built.

Adding instructions to the UI

sidebarPanel(
  h4("Simulation settings"),
  sliderInput("n", "Sample size", min = 2, max = 500, value = 30),
  helpText("Larger values of n produce a narrower sampling distribution."),
  selectInput("dist", "Population", choices = c("Normal", "Exponential")),
  helpText("The central limit theorem applies regardless of population
            shape when n is sufficient.")
)

Rules: every control needs a label; every non-obvious control needs helpText.

Organising for usability

  • Group related controls in wellPanel
  • Put the most-used controls at the top
  • Use h4 headings to name sections of a long sidebar
  • Put explanatory text near the output it describes, not in the sidebar
  • Name the application with titlePanel

Progress feedback

results <- reactive({
  withProgress(message = "Running simulation...", value = 0, {
    out <- replicate(input$reps, {
      incProgress(1 / input$reps)
      mean(rnorm(input$n))
    })
    out
  })
})
  • withProgress creates a progress bar in the browser
  • incProgress advances it by a fraction
  • Use for any computation that takes more than about one second

Deploying to shinyapps.io

  1. Create an account at shinyapps.io
  2. Install rsconnect: install.packages("rsconnect")
  3. In RStudio, open app.R and click Publish
  4. Connect your shinyapps.io account by pasting the token
  5. Choose an application name and click Publish

The free tier: up to 5 applications, 25 active hours per month.

Deployment from the console

rsconnect::deployApp(appDir = "path/to/app")

Equivalent to clicking Publish. Useful for automation or when RStudio is not available.

Common errors and how to fix them

Error Likely cause Fix
“no active reactive context” input$x read outside reactive Move into render* or reactive
Output blank on startup input$x is NULL Add req(input$x)
Output never updates Input read inside isolate Remove isolate or check the render block
Deployment fails Package not listed Add package to install.packages at top of app.R

Debugging: the hierarchy

  1. cat / print — fastest, good for confirming values
  2. req — fixes NULL/blank output errors
  3. browser() — interactive inspection inside a reactive block
  4. reactlog — full visual picture of the reactive graph

Start with 1; escalate only if needed.

Where to go next: Shiny modules

Modules are the answer to “my server function is 500 lines long”.

counterUI <- function(id) {
  ns <- NS(id)
  tagList(actionButton(ns("btn"), "Click"), textOutput(ns("count")))
}
counterServer <- function(id) {
  moduleServer(id, function(input, output, session) {
    n <- reactiveVal(0)
    observeEvent(input$btn, n(n() + 1))
    output$count <- renderText(n())
  })
}

Each module has its own namespace — IDs cannot clash with the rest of the app.

Where to go next: DT tables

DT::dataTableOutput("tbl")
output$tbl <- DT::renderDataTable(mtcars)
  • Sortable, filterable, paginated tables in one line
  • Row selection in a DT table is a reactive value you can use elsewhere
  • Far more capable than renderTable for data exploration

Where to go next: plotly

plotly::ggplotly(my_ggplot)
  • Converts any ggplot to an interactive plot with tooltips, zoom, and pan
  • Hover events can be used as reactive inputs
  • Minimal additional code required

Where to go next: bslib

ui <- page_sidebar(
  title = "My app",
  sidebar = sidebar(...),
  layout_columns(card(...), card(...))
)
  • Modern Bootstrap-based layout system from Posit
  • Cards, value boxes, sidebars, navigation
  • Integrates with theming via bs_theme

Summary

  • A polished app needs helpText, clear labels, and explanatory text near outputs
  • withProgress provides browser-level feedback for slow computations
  • shinyapps.io deployment takes about five minutes for a working application
  • The standard debugging hierarchy: print → req → browser → reactlog
  • Next topics: modules, DT, plotly, bslib, performance optimisation