Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.
Short introduction to Shiny modules
Building complex Shiny application can lead to code duplication.
Shiny modules are the solution to this code duplication.
A Shiny module is a piece of a Shiny app reusable inside more complex app.
Thus, a module can be seen as a function that could be called multiple times rather than copy-paste some code.
A Shiny application is defined into several files :
ui.R
file for interface.server.R
file for server logic.- OR a single
app.R
file for both interface & server logic.
Exactly like a Shiny application, a module is defined with 2 functions :
- One for interface (suffixed by ‘
UI
’ or ‘_UI
’, ex =myModule_UI
) - One for server logic (ex =
myModule
)
In this article is described other positive features of modules, appropriate situations to their use and an hello-world example.
NB : This Joe Cheng post is a good introduction to Shiny modularization.
Use module multiple times inside an application
As soon as I need to rewrite the same UI outputs and/or server logic functions. I create a module and use it as much time as needed.
In the exemple below, the module createPlot
is used with datasets :
dataset_1
dataset_2
dataset_3
NB : The module createPlot
is defined by functions :
createPlot
defining server logic.createPlotUI
defining interface.
Use module across applications
During Shiny development, sometime I need a “piece of app” that I have already done somewhere else.
Here’s my process :
- Transform these “pieces of app” into modules (cf 2 functions :
interface
&server logic
). - Gather them inside an R package.
- Use these modules into multiple applications.
This solution offers all the conveniance from packaging such as :
- Update the R package will update all of my Shiny applications.
- Add tests on the R package with
testthat
. - Create documentation.
- etc…
NB : Because of reasons above, merging modules into R package makes sens even if they are used only into one Shiny application.
To reorganize Shiny files
As you know, all Shiny code can be stored in server.R
& ui.R
files or in a single app.R
file.
Using modules to split the code into different files can help :
- To have a better understanding of application architecture.
- To collaborate with other colleagues.
Use case 1 : Re-use module inside application
Non modularized application example :
app |___ server.R (1000 lines) |___ ui.R (500 lines)
Creation of 2 modules (module_1
& module_2
), both used n times into application1
:
application1 |___ server.R (300 lines) |___ ui.R (200 lines) | |___ modules |___ module_1.R (200 lines) |___ module_2.R (200 lines)
After the module creation, you can notice a better organization of files and also a code line saving (as soon as modules are re-used at least 2 times).
Use case 2 : Modules availability through packages
Non modularized applications example :
application1 application2 application3 |___ server.R (400 lines) |___ server.R (400 lines) |___ server.R (400 lines) |___ ui.R (250 lines) |___ ui.R (250 lines) |___ ui.R (250 lines)
Create a package with module_1
reused in each applications :
packageModules |___ module_1.R (350 lines) application1 application2 application3 |___ server.R (200 lines) |___ server.R (200 lines) |___ server.R (200 lines) |___ ui.R (150 lines) |___ ui.R (150 lines) |___ ui.R (150 lines)
We can see here a reduce of code lines. We also get advantages of packaging as noted above.
Summary
Good reasons to use Shiny modules :
- Replicate “piece of app” multiple times inside a Shiny application.
- Replicate “piece of app” accross different Shiny application.
- Reorganize code files for more clarity & understanding.
- Merge modules inside an R package and get advantages of R packaging.
Hello Shiny module world
Here below a basic Shiny application containing a minimal Hello World example. The code is also available on this repository.
You can launch the application locally with :
shiny::runGitHub(repo = "ardata-fr/shinyapps", subdir = "modules-hello-world")
app.R
library(shiny) # load module functions source("hello_world.R") ui <- fluidPage( titlePanel("Using of Shiny modules"), fluidRow( # Call interface function of module "hello_world" hello_worldUI(id = "id_1") ) ) server <- function(input, output, session) { # Call logic server function of module "hello_world" callModule(module = hello_world, id = "id_1") } shinyApp(ui = ui, server = server)
hello_world.R
# Function for module UI hello_worldUI <- function(id) { ns <- NS(id) fluidPage( fluidRow( column(2, textInput(ns("TI_username"), label = NULL, placeholder = "your name")), column(2, actionButton(ns("AB_hello"), label = "Hello !")) ), hr(), fluidRow( column(12, textOutput(ns("TO_Hello_user"))) ) ) } # Function for module server logic hello_world <- function(input, output, session) { # When user clicks on "Hello" button : Update reactive variable "name" name <- eventReactive(input$AB_hello, { return(input$TI_username) }) # Show greetings output$TO_Hello_user <- renderText({ if (name() %in% "") { return("Hello world !") } else { return(paste("Hello", name(), "!")) } }) }
To be continued
Now you know Shiny modules are great, you can get our minimal example to start with.
The next post will explain the data workflow between Shiny application and module(s).
R-bloggers.com offers daily e-mail updates about R news and tutorials about learning R and many other topics. Click here if you're looking to post or find an R/data-science job.
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.