Skip to contents

Using shinyrepro

There is a single exported function, repro, that takes a reactive object and converts it into a script that can be reused outside of the Shiny application to reproduce the result of the reactive. This can be sent to a simple verbatimTextOutput or something more UX friendly such as the highlighter package to display the script in the UI.

Best Practices

Bind repro call to Event

The code to reproduce a given reactive will be updated whenever an input or reactive feeding into the provided reactive is updated, therefore it is recommended to have the reactive as an event attached.

width_range <- reactive({
  iris_filt <- dplyr::filter(iris, Species == "versicolor")
  range(iris_filt$Petal.Width)
})

# Good
repro_range <- reactive(repro(width_range)) |>
  bindEvent(width_range())
  
# Bad
repro_range <- reactive(repro(width_range))

Put Side-Effects in Observers

This is general best-practice when developing Shiny applications, but avoid putting code for its side effects in reactive expressions, and instead create smaller reactive calls, and have observers running the code not intended for reproducing outputs.

# Good
width_range <- reactive({
  iris_filt <- dplyr::filter(iris, Species == "versicolor")
  range(iris_filt$Petal.Width)
})

observe({
  updateSliderInput("width", width_range())
})

# Bad
width_range <- reactive({
  iris_filt <- dplyr::filter(iris, Species == "versicolor")
  widths <- range(iris_filt$Petal.Width)
  updateSliderInput("width", widths)
  widths
})

summary_data <- reactive({
  iris_filt <- dplyr::filter(iris, Species == "versicolor")
  shinyjs::toggle("table", condition = nrow(iris_filt) > 0L)
  dplyr::summarise(iris_filt, dplyr::across(where(is.numeric), mean))
})

Create a Business Logic Package

In order that developers can easily recreate outputs generated in Shiny applications, add any business logic, such as ETL, data manipulation and modelling, to a separate package. This will allow users to recreate the tables and plots generated in the app without having to install all the packages associated with the application.

Use Secrets in Reactives

If you are using secrets, such as environment variables, make sure that they are defined within a reactive expression. If it is defined in the module, or in the global environment, the secret will be written in the assignment.

# Good
moduleServer(id, function(input, output, session) {
  my_reactive <- reactive({
    api_key <- Sys.getenv("MY_API_KEY")
    ...
  })
})

# Bad
moduleServer(id, function(input, output, session) {
  api_key <- Sys.getenv("MY_API_KEY")
  
  my_reactive <- reactive({
    ...
  })
})