Package {shinyreprex}


Type: Package
Title: Reproducible Code for 'Shiny' Objects
Version: 0.3.0
Description: Provides functionality to extract reactive expressions from a 'shiny' application and convert them into stand-alone R scripts. This enables users to reproduce tables and visualisations outside the interactive UI, facilitating integration into static reports or automated workflows without requiring access to the original application source code.
License: MIT + file LICENSE
URL: https://github.com/AscentSoftware/shinyreprex, https://ascentsoftware.github.io/shinyreprex/
BugReports: https://github.com/AscentSoftware/shinyreprex/issues
Depends: R (≥ 4.3.0)
Imports: S7, cli, constructive, purrr, renv, rlang, styler
Suggests: dplyr, knitr, rmarkdown, shiny, spelling, testthat (≥ 3.0.0)
VignetteBuilder: knitr
Config/Needs/website: rmarkdown, ascentsoftware/acuitydown
Config/roxygen2/version: 8.1.0
Config/testthat/edition: 3
Encoding: UTF-8
Language: en-GB
Collate: 'Repro_S7.R' 'S7_utils.R' 'repro_call_chunk.R' 'call_chunk_function.R' 'call_chunk_generic.R' 'call_chunk_if.R' 'call_chunk_null.R' 'call_chunk_reactive.R' 'call_chunk_reactval.R' 'call_chunk_shiny.R' 'call_chunk_subset.R' 'call_chunk_switch.R' 'call_chunk_utils.R' 'repro_chunk.R' 'chunk_call.R' 'chunk_generic.R' 'chunk_reactive.R' 'package.R' 'reactive_expression.R' 'register_reactives.R' 'reprex_lockfile.R' 'reprex_reactive.R' 'walk_packages.R'
NeedsCompilation: no
Packaged: 2026-09-17 17:49:52 UTC; BaldAsh
Author: Ashley Baldry [aut, cre]
Maintainer: Ashley Baldry <ashley.baldry@acuityanalytics.com>
Repository: CRAN
Date/Publication: 2026-09-17 18:10:02 UTC

Reproducible Code

Description

An S7 object that holds the code and packages required to re-create a given reactive.

Usage

Repro(code = list(), packages = character(0), prerequisites = list())

Arguments

code

Code chunks found in a given expression

packages

Packages found in the function calls in the code and/or pre-requisites

prerequisites

Code chunks used to generate reactive objects found in the code


Reactive Object Check

Description

Confirm every object supplied is an unevaluated reactive, raising an error that names the calling function so the message points at the user's call.

Usage

assert_reactives(reactives, fn_name)

Arguments

reactives

A list of objects to check

fn_name

Name of the calling function, used in the error message

Value

NULL, invisibly. Called for its side effect of raising an error.


Environment of a Called Reactive

Description

Find the environment holding the reactive a call refers to, checking the expression's own environment before the environment it was passed in from.

Usage

call_reactive_env(expr, env)

Arguments

expr

An expression to check

env

The environment the expression belongs to

Value

The environment holding the reactive, or NULL if the call is not to a reactive.


Custom S7 Classes

Description

Additional classes to include in S7 to use in repro_code and repro_code_chunk methods:

Reactives

These variables need to be handled in a specific way to extract non-static values stored in reactive calls.

class_reactive

The class capturing shiny::reactive() calls

class_event_cache

The class capturing shiny::bindCache() calls

class_event_reactive

The class capturing shiny::bindEvent() calls

class_bind_reactive

The union of class_event_cache and class_event_reactive

Special Functions

When determining evaluating a chunk, the function name gets attached to the class of the chunk, these are special cases that need to be handled in a non-standard way.

class_call_function

The class capturing anonymous function definitions

class_call_reactive

The class capturing evaluated shiny::reactive() objects

class_call_reactval

The class capturing evaluated shiny::reactiveValues() objects

class_call_reactval_setter

The class capturing shiny::reactiveVal() setter calls, which are skipped with a warning as they cannot be reproduced outside of Shiny

class_call_if

The class capturing if calls

class_call_switch

The class capturing switch calls

class_call_null

The class capturing undefined calls, such as pkg::fn

class_call_shiny

The class capturing ignorable shiny function calls such as shiny::req() and shiny::validate()

class_call_subset

The class capturing a subset ($) or a double-bracket subset ([[) call


Reactive Variables Definition Check

Description

A helper function to check whether or not the reactive variables to be added to the Repro object already exists. Used to avoid duplicate definitions being added to a script.

Usage

is_new_reactive(new, exisitng)

Arguments

new, exisitng

A named list of reactive variable definitions

Value

A boolean stating whether or not there is at least one reactive definition in new that doesn't exist in existing


Call Checks

Description

A set of helper functions that determine what type of call is being made within an expression.

is_reactive_call checks whether or not the call is evaluating a shiny::reactive variable.

is_reactive_val_call checks whether or not the call is evaluating a shiny::reactiveVal variable.

is_reactive_val_setter_call checks whether or not the call is setting the value of a shiny::reactiveVal variable.

is_reactive_values_call checks whether or not the call is evaluating an item within a shiny::reactiveValues variable.

is_any_reactive_call checks whether or not the call points to evaluating a reactive, reactiveVal or reactiveValues.

is_variable_call checks whether or not the call point to a variable that is defined within the given module.

is_input_call checks whether or not the call points to evaluate an input value.

is_session_user_data checks whether or not the call points to evaluate an object within session$userData

Usage

is_reactive_call(x, env = rlang::caller_env())

is_reactive_val_call(x, env = rlang::caller_env())

is_reactive_val_setter_call(x, env = rlang::caller_env())

is_reactive_values_call(x, env = rlang::caller_env())

is_any_reactive_call(x, env = rlang::caller_env())

is_variable_call(x, existing_vars = NULL, env = rlang::caller_env())

is_input_call(x)

is_session_user_data(x)

Arguments

x

An R call object

env

The environment the call is being made, by default it is the environment calling the check, but is likely the environment the call is being made i.e. the reactive expression.

existing_vars

A character vector of variable definitions that exist in the Repro object

Value

A boolean value determining whether or not the call check has passed.


Reactive Expression and Environment

Description

Extract the unevaluated body of a reactive, along with the environment it was assigned in.

Environments

The Observable object attached to the given reactive is extracted. Within the Observable, the .origFunc contains the environment that the reactive expression was created - the parent environment being the module that the reactive is assigned in. This allows the variables in the module to be found and set as pre-requisites for the given reactive.

If bindCache or bindEvent are used, then the environment found is the call within the relevant function. To get to the module environment, we find that the reactive is assigned as "wrappedFunc", so that is used to find the module environment.

Usage

reactive_expression(x)

Arguments

x

A shiny::reactive object

Value

A list with two elements: body, the unevaluated body of the reactive, and env, the module environment the reactive was assigned in.


Register Reactives for Reproduction

Description

Record reactives against the current Shiny session, so that reprex_packages() and reprex_lockfile() can be called with no arguments and still cover the whole application.

This avoids having to return reactives out of every module purely so a single top-level call can see them. Each module registers what it owns, and the session holds the collection.

Namespacing

Registrations are namespaced by the calling module, so two modules may register reactives of the same name without collision, and re-registering the same name in the same module replaces the previous entry rather than adding a duplicate.

When to Register

Registering does not evaluate the reactive. Packages are resolved by reading the expression held in the reactive, so it may be registered while still gated behind shiny::req() or inputs that have yet to be set. Registering at module setup is therefore both safe and preferred, as it does not depend on the user having visited the output first.

Session Scope

The collection lives on the session, so it is discarded when the session ends and is never shared between concurrent users of the same application.

Usage

register_reactives(..., session = shiny::getDefaultReactiveDomain())

Arguments

...

One or more shiny::reactive objects to register. Names are taken from the arguments, so register_reactives(summary_tbl) registers under "summary_tbl". Supply names explicitly to override.

session

The Shiny session to register against. Defaults to the current reactive domain, which inside a module is that module's session.

Value

The registered reactives, invisibly, as a named list.

See Also

reprex_packages() and reprex_lockfile(), which read the registered collection when called with no reactives.

Examples


library(shiny)

summaryServer <- function(id) {
  moduleServer(id, function(input, output, session) {
    summary_tbl <- reactive(purrr::keep(iris, is.numeric))

    register_reactives(summary_tbl)

    summary_tbl
  })
}

# Elsewhere in the application, with no reactives threaded through:
# reprex_lockfile()


Registered Reactives

Description

The reactives registered against a session by register_reactives().

Usage

registered_reactives(session)

Arguments

session

A Shiny session object, or NULL when called outside Shiny

Value

A named list of reactives, empty if none have been registered.


Create a Lockfile to Reproduce Reactives

Description

Capture the exact package versions, sources and R version needed to reproduce one or more shiny::reactive objects, as an renv lockfile. Restoring the lockfile with renv::restore() recreates the environment the reactives were generated in, including the recursive dependency tree.

A single lockfile covers every reactive passed, so an application can offer one download that reproduces all of its outputs.

Snapshot Scope

The snapshot is taken from the currently loaded library (.libPaths()), so the versions recorded are exactly those the running Shiny session used to produce the reactives. renv resolves and records the full recursive dependency tree of packages, so only the top-level set needs to be supplied.

Isolation

The snapshot runs against a throwaway temporary project, so it never writes renv infrastructure into, or otherwise modifies, the application's own directory.

Output Path

A relative lockfile is resolved against the working directory, not against the temporary project used for the snapshot. The default therefore writes renv.lock into whichever directory the application is running from. Pass an absolute path to control where it lands; inside a shiny::downloadHandler() that is the file argument.

Usage

reprex_lockfile(
  ...,
  packages = NULL,
  lockfile = "renv.lock",
  exclude = NULL,
  session = shiny::getDefaultReactiveDomain()
)

Arguments

...

One or more shiny::reactive objects to reproduce. If none are supplied, every reactive registered by register_reactives() is used, so a modular application can offer a whole-app lockfile without threading reactives up to the top level.

packages

Character vector of package names to snapshot. If NULL (the default), every package detected across ... is used. Supplying this lets the user narrow the set, or add a package the detector could not find (for example one attached only for an operator or method).

lockfile

Path to write the lockfile to. Defaults to "renv.lock".

exclude

Character vector of package names to omit from the snapshot.

session

The Shiny session to read registered reactives from. Only used when ... is empty and packages is NULL.

Value

The absolute path the lockfile was written to, invisibly.

See Also

reprex_packages() to inspect the detected set without writing a lockfile, and register_reactives() to record reactives from within each module.

Examples


library(shiny)

numeric_iris <- reactive(purrr::keep(iris, is.numeric))

isolate(reprex_lockfile(numeric_iris, lockfile = tempfile(fileext = ".lock")))


Packages Required to Reproduce Reactives

Description

Extract the set of non-base packages needed to reproduce one or more shiny::reactive objects, with duplicates removed across every reactive passed.

Use this when building a custom UI around reprex_lockfile(): it gives you the list of detected packages to present to the user for selection.

Packages are found by reading the expression held in each reactive rather than by generating its script, so every branch of an if or switch contributes. The result may therefore be a superset of the library() calls reprex_reactive() emits for the branch actually taken, on the basis that a lockfile is safer holding a package that is not needed than missing one.

Usage

reprex_packages(..., session = shiny::getDefaultReactiveDomain())

Arguments

...

One or more shiny::reactive objects to inspect. If none are supplied, every reactive registered against session by register_reactives() is used.

session

The Shiny session to read registered reactives from. Only used when ... is empty. Defaults to the current reactive domain.

Value

A character vector of unique package names. Base packages are excluded, as they require no installation.

See Also

reprex_lockfile() to turn this set into an renv lockfile, and register_reactives() to record reactives from within each module.

Examples


library(shiny)

numeric_iris <- reactive(purrr::keep(iris, is.numeric))
styled_code <- reactive(styler::style_text("1 + 1"))

# Outside a running application, isolate supplies the reactive context
isolate(reprex_packages(numeric_iris, styled_code))


Reproduce Code

Description

Construct the code within a given shiny::reactive object to be able to re-create the output outside of a Shiny session.

Usage

reprex_reactive(x)

Arguments

x

shiny::reactive object to make reproducible

Value

A character string, that when printed (using base::cat), displays the script that reproduces the contents of x.

Examples

library(shiny)

ui <- fluidPage(
  h1("Reproducible Code Example"),
  inputPanel(
    sliderInput(
      "min_width",
      "Minimum Petal Width",
      min(iris$Petal.Width),
      max(iris$Petal.Width),
      min(iris$Petal.Width),
      step = 0.1
    ),
    selectInput(
      "summary_fn",
      "Summary Function",
      c("Mean" = "mean", "Median" = "median", "SD" = "sd"),
      selected = "mean"
    )
  ),
  fluidRow(
    column(
      width = 5,
      h2("Table"),
      tableOutput("table")
    ),
    column(
      width = 7,
      h2("Code"),
      verbatimTextOutput("code")
    )
  )
)

server <- function(input, output, session) {
  iris_filt <- reactive({
    iris[with(iris, Petal.Width > input$min_width), ]
  })

  summary_tbl <- reactive({
    aggregate(
      Sepal.Width ~ Species,
      data = iris_filt(),
      FUN = get(input$summary_fn)
    )
  })

  output$table <- renderTable(summary_tbl())
  output$code <- renderText(reprex_reactive(summary_tbl))
}

if (interactive()) {
  shinyApp(ui, server)
}


Session Reactive Store

Description

Fetch (creating on first use) the environment holding this session's registered reactives. An environment is used so that registrations from module servers mutate a single shared collection.

Usage

reprex_store(session)

Arguments

session

A Shiny session object

Value

An environment with a reactives element, a named list of reactives.


Reproduce Code Chunk

Description

Evaluate a chunk of code to extract Shiny inputs and reactives, replacing the inputs with the values selected by the user, and the reactives with the code bodies used to generate them.

Usage

repro_chunk(x, repro_code = Repro(), env = rlang::caller_env())

Arguments

x

shiny::reactive() object to make reproducible

repro_code

A Repro object to store calls found in x. By default it is empty, but if x is not the first call within an expression, this will have prior calls and pre-requisites that might be used in x.

env

The environment x is defined in. By default it is the environment of where reprex_reactive is called

Details

Whilst a default is provided to env, it is unlikely that this is the same environment x is defined in. This allows the top-level reprex_reactive call to pass through environments found for calls to other reactives in the chunk.

Value

A Repro object containing all the necessary code and packages to recreate the provided expression when evaluated.


Collect Packages From an Expression

Description

Walk an expression and collect the packages of every call found within it, without reproducing any of the code. This is a deliberately cheap alternative to repro_chunk() for the cases that only need the package list, such as reprex_packages() and reprex_lockfile().

Why Not Reproduce the Code

Reproducing a chunk substitutes reactive and input values into the expression, which means constructing every module variable as code via constructive::construct. For a module holding a data frame that dominates the run time, and all of it is discarded when only the packages are wanted.

Branches Are Not Evaluated

Every branch of an if or switch is walked, rather than evaluating the condition and following only the branch that would be taken. A lockfile is safer for containing a package that turns out not to be needed than for missing one, and not evaluating keeps the walk both faster and independent of the current input values.

Usage

walk_packages(expr, env, seen = character(), packages = character())

Arguments

expr

An expression to walk

env

The environment the expression belongs to, used to identify calls to other reactives

seen

Names of reactives already walked, preventing a reactive that is referenced more than once from being walked repeatedly

packages

Packages collected so far

Value

A character vector of unique package names, excluding base packages.