Shiny for R
The R and Python Shiny packages are quite similar, and as a result if you know how to build a Shiny app in R you are well on your way to building one in Python. All of the main components of Shiny like reactivity, rendering functions, and modules are the same, and both packages use the same JavaScript code. There are, however, a few differences that you need to keep in mind in order to build effective Shiny applications in Python. If you’re reading this, we expect that you are an existing R Shiny user with some Python knowledge.
Shiny express is a new, more expressive, way to build PyShiny apps. It is not available in R, so the comparisons drawn below are only relevant to core (i.e., non-express) apps.
Getting started
R users tend to use the R console to install and run Shiny while Python requires you to use the terminal. To get started you can do the following (or see the installation instructions for a more in-depth explanation):
- In your terminal, create a new directory with
mkdir <my_directory>
and navigate into it withcd <my_directory>
- Install Shiny. We strongly recommend using a virtual environment for this as it will eliminate dependency resolution headaches and make deployment easier.
- Call
shiny create .
to create an example shiny app in your directory - Call
shiny run --reload
to run the app and reload when the source code changes
- Always use a virtual environment with Python projects
- Install and run shiny from the command line, not a Python process
Syntax differences
There are five main syntax difference between the R and Python versions of Shiny:
- Decorators instead of render functions
- Function names are used to connect outputs to the UI
- More precise namespaces
- All inputs are invoked with
input.<input_name>()
- Some functions have different names
Decorators
Shiny for Python uses decorators instead of top level rendering functions. Decorators are just python functions which take other functions and are invoked by putting @<decorator>
above the function definition. While R doesn’t have an exact analog to decorators they are similar to function operators you like purrr::safely
.
- Use rendering decorators like
@render.plot
,@render.text
, or@render.ui
instead ofrenderPlot()
,renderText
, orrenderUI
- Reactive calculations (equivalent to reactive expressions in R) are decorated
@reactive.calc
, and reactive effects (equivalent to observers in R) are decorated with@reactive.effect
.
Connecting outputs
Both R an Python use a special object type to connect server computations to UI components, but the interface is quite different. In R, we connect outputs to UI elements by assigning into the output
object but since Python renderings are produced with decorators, we instead use the function name to connect the rendered object to its UI component.
- In Python, we don’t define outputs by assigning to
output$x
- Use the function name to connect a server output to a UI element
R
Python
Submodules
All of the Shiny R functions are in a single package namespace. On the Python side we make use of submodules to keep related functions together. Note that “submodules” in this case refers to the generic module which is not the same as shiny modules. For example, instead of sliderInput()
, you would call ui.input_slider()
, where the ui.
refers to a submodule of the main shiny
module.
- Python submodules make autocomplete easier
- Some important namespaces include:
- ui for the UI elements
- render includes the rendering decorators
- reactive has reactive expressions and observers
R
Python
Call inputs with ()
In R reactive values and reactive expressions are retrieved with different syntax. Reactive values like input$value
are retrieved like variables while reactive expressions are called like functions my_reactive()
.
This interface makes it seem like inputs and reactive expressions are different types of thing when in fact they’re the same type of thing. In Python we chose to require that all reactive objects be retrieved with a function call. So instead of calling input.value
you use input.value()
.
R
Python
- Access input values by calling the object like a function
input.x()
, notinput$x
Function name changes
The Python function names have been modified to make them easier to discover with tab completion. For example all python output functions start with output_
while the input functions start with input_
. This means that you can type ui.ou
and hit tab to see all of the available output functions. The Shiny R functions on the other hand all start with the element type (plotOutput
, textInput
), which makes it hard to see all of the input or output options. For the most part you can follow this naming pattern to find the function you’re looking for, but there are a number of functions that have different names in R and Python, the most important of which are listed below:
R Function | Python Equivalent |
---|---|
observe |
@reactive.effect |
reactive |
@reactive.calc |
bindEvent |
@reactive.event |
reactiveEvent |
@reactive.calc with @reactive.event |
observeEvent |
@reactive.effect with @reactive.event |
htmlTemplate |
page_template |
tabPanelBody |
navs_content |
*Tab (insertTab , appendTab , etc) |
nav_* (nav_insert , nav_append etc) |
fluidRow |
row |
Reactive programming
Reactivity works mostly the same in R and Python, but there are a few small differences in naming and syntax.
New names for reactive()
and observe()
In Shiny for R, reactive expressions (created by reactive()
, which are used when you want to compute a value (which is then used in an output or an observer), and observers (created by observe()
) are used for their side effects, like writing data to disk. This is a common source of confusion because the names reactive()
and observe()
do not clearly express when they should be used. To help clarify this confusion we’ve renamed reactive()
to @reactive.calc
, and observe()
to @reactive.effect
in Python.
R
library(shiny)
ui <- fluidPage(
sliderInput("n", "N", 0, 100, 40),
verbatimTextOutput("txt"),
actionButton("reset", "Reset")
)
server <- function(input, output, session) {
val <- reactive({input$n})
observe({
input$reset
updateSliderInput(session, "n", value = 40)
})
output$txt <- renderText({
paste0("n*2 is ", val()," * 2")
})
}
shinyApp(ui, server)
Python
from shiny import App, reactive, render, ui
app_ui = ui.page_fluid(
ui.input_slider("n", "N", 0, 100, 40),
ui.output_text_verbatim("txt"),
ui.input_action_button("reset", "Reset"),
)
def server(input, output, session):
@reactive.calc
def val():
return input.n()
@reactive.effect
def _():
input.reset()
ui.update_slider("n", value=40)
@render.text
def txt():
return f"n*2 is {val() * 2}"
app = App(app_ui, server)
Reactive Values
In R, there are two types of reactive objects which store values:
Items in a
reactiveValues
object. This is a list-like object that contains multiple reactive values. (Note that theinput
object is areactiveValues
.)Standalone
reactiveVal
objects.
In R, the way that you get values from a reactiveValues
object differs from how you get it from a reactiveVal
. To get the value of an item’s in a reactiveValues
object, you would simply access it with input$x
. However, for a standalone reactiveVal
, you would invoke it like a function, with x()
.
In Shiny for Python, we’ve simplified things in the following ways:
There is no direct analog to R’s
reactiveValues
.The analog of R’s standalone
reactiveVal
isreactive.value
. (Theinput
object in Python is a dictionary-like object containing individualreactive.value
objects.)Reactive values have can be retrieved with
my_val()
ormy_val.get()
and can be set withmy_val.set()
.
There is no analog of reactiveValues
in Python, but you can create something similar by using a dictionary of reactive.value
objects.
R
library(shiny)
ui <- fluidPage(
actionButton("minus", "-1"),
actionButton("plus", "+1"),
br(),
textOutput("value")
)
server <- function(input, output, session) {
value <- reactiveVal(0)
observeEvent(input$minus, {
newValue <- value() - 1
value(newValue)
})
observeEvent(input$plus, {
newValue <- value() + 1
value(newValue)
})
output$value <- renderText({
value()
})
}
shinyApp(ui, server)
Python
from shiny import *
app_ui = ui.page_fluid(
ui.input_action_button("minus", "-1"),
ui.input_action_button("plus", "+1"),
ui.br(),
ui.output_text("value"),
)
def server(input: Inputs, output: Outputs, session: Session):
val = reactive.value(0)
@reactive.effect
@reactive.event(input.minus)
def _():
newVal = val() - 1
val.set(newVal)
@reactive.effect
@reactive.event(input.plus)
def _():
newVal = val() + 1
val.set(newVal)
@render.text
def value():
return str(val())
app = App(app_ui, server)
Mutability
One of the biggest differences between R and Python is the mutability of objects.
In R, most objects are immutable. This means, for example, that if you pass a data frame to a function, that function cannot alter your copy of the data frame.
In Python, many objects are mutable. This means that they can be modified in place—modifying an object in one part of a program can cause it to be (unexpectedly) modified in another part of the program.
This difference in mutability has consequences for Shiny applications and for programming in general. In many cases, using .copy()
in Python will help avoid bugs due to inadvertently altering mutable objects. See our page on mutability to understand the possible bugs and how to avoid them.