Scoping rules for Shiny apps
Scoping
Where you define objects will determine where the objects are visible. There are three different levels of visibility that you’ll want to be aware of when writing Shiny apps. Some objects are visible within the server
code of each user session; other objects are visible in the server
code across all sessions (multiple users could use a shared variable); and yet others are visible in the server
and the ui
code across all user sessions.
This document describes how scoping works within a single R process. One R process can support multiple Shiny sessions. Some hosting platforms (including Posit Connect, Shiny Server Pro, and shinyapps.io) also allow running multiple R processes to handle heavier traffic. Within each R process, the scoping works as explained below, but between the R processes, no objects are shared. So, for example, if you configure Posit Connect to start a new R process for each connection to your app, no objects will ever be shared between different sessions of the app, since these sessions all belong to different R processes. (To learn more about about this, and how it affects the performance of your apps, see this article.)
Per-session objects
In app.R
, the server
function takes three arguments: input
, output
and session
.
<- function(input, output, session) {
server # Server code here
# ...
) }
The function is called once for each session. In other words, the server
function is called each time a web browser is pointed to the Shiny application.
Everything within this function is instantiated separately for each session. This includes the input
, output
and session
objects that are passed to it: each session has its own input
, output
and session
objects, visible within this function.
Other objects inside the function, such as variables and functions, are also instantiated for each session. In this example, each session will have its own variable named startTime
, which records the start time for the session:
<- function(input, output, session) {
server <- Sys.time()
startTime
# ...
}
Objects visible across all sessions
You might want some objects to be visible across all sessions. For example, if you have large data structures, or if you have utility functions that are not reactive (ones that don’t involve the input
or output
objects), then you can create these objects once and share them across all user sessions (within the same R process), by placing them in app.R
, but outside of the server
function definition.
For example:
# A read-only data set that will load once, when Shiny starts, and will be
# available to each user session
<- read.csv("bigdata.csv")
bigDataSet
# A non-reactive function that will be available to each user session
<- function(x) {
utilityFunction # Function code here
# ...
}
<- function(input, output, session) {
server # Server code here
# ...
}
You could put bigDataSet
and utilityFunction
inside the server
function, but doing so will be less efficient, because they will be created each time a user connects.
If the objects change, then the changed objects will be visible in every user session. But note that you would need to use the <<-
assignment operator to change bigDataSet
, because the <-
operator only assigns values in the local environment.
<- 1
varA <- 1
varB <- list(X = 1, Y = 2)
listA <- list(X = 1, Y = 2)
listB
<- function(input, output, session) {
server # Create a local variable varA, which will be a copy of the shared variable
# varA plus 1. This local copy of varA is not be visible in other sessions.
<- varA + 1
varA
# Modify the shared variable varB. It will be visible in other sessions.
<<- varB + 1
varB
# Makes a local copy of listA
$X <- 5
listA
# Modify the shared copy of listB
$X <<- 5
listB
# ...
}
Things work this way because app.R
is sourced when you start your Shiny app. Everything in this script is run immediately. However, your server
function is only actually called when a web browser connects and a new session is started
Global objects
Objects defined in global.R
are similar to those defined in app.R
outside of the server
function definition, with one important difference: they are loaded into the global environment of the R session; all R code in a Shiny app is run in the global environment or a child of it.
In practice, there aren’t many times where it’s necessary to share variables between server
and ui
. The code in ui
is run once, when the Shiny app is started and it generates an HTML file which is cached and sent to each web browser that connects. This may be useful for setting some shared configuration options.
Scope for included R files
If you want to split the server
or ui
code into multiple files, you can use source(local = TRUE)
to load each file. You can think of this as putting the code in-line, so the code from the sourced files will receive the same scope as if you copied and pasted the text right there.
This example app.R
file shows how sourced files will be scoped:
# Objects in this file are shared across all sessions in the same R process
source('all_sessions.R', local = TRUE)
<- function(input, output, session) {
server # Objects in this file are defined in each session
source('each_session.R', local = TRUE)
$text <- renderText({
output# Objects in this file are defined each time this function is called
source('each_call.R', local = TRUE)
# ...
}) }
If you use the default value of local = FALSE
, then the file will be sourced in the global environment.