Shiny Apps
Shiny apps are cool, but they can get complicated, and frustrating to debug. Complicated apps–longer than 500 lines of code–can have subtle paradoxes caused by reactive elements. It is helpful to think carefully about how to modularize apps, much as we do with functions, so that we can debug piece by piece.
R language apps developed with collaborators by Brian Yandell using the Shiny package illustrate in various ways an evoling design over time, which made the apps easier to use, understand and debug. These repos include some lessons learned that hopefully will help others as they design and evolve apps for this project. For those interested in comparing shiny apps built in R vs. python, there is a useful development in the Shiny Geyser App repo.
The best reference for building shiny apps is Mastering Shiny, with particular attention to Ch 19 on Shiny modules. See also Modularizing Shiny app code. Yandell’s key working repos that inform this document are
- Helper Apps
- Shiny app for QTL visualization
- Shiny Geyser App with and without Modules
- Founder Shiny App
- Shiny app for R/qtl2
- Learning about Shiny Modules
Below are discussions in reverse chronological order of these repos:
- helperApps: modules for reuse in other Shiny Apps
- qtlApp: modular QTL visualization app
- geyser: modular concepts and construction
- foundrShiny: pragmatic code reuse driven by collaborators
- qtl2shiny: localized QTL analysis and visualization
- shiny_module: learning about reactivity
Additional links on R in general can be found at R for Data Sciences.
byandell.github.io/Documentation
qtlApp: modular QTL visualization app
This is an ongoing project that is designed for QTL visualization and analysis at scale. It is organized as a package with multiple small shiny modules, each with its own app. The goal is to make this straightforward and easy enough for team members to develop their own modules as the tools evolve.
The package has several analysis files used by shiny modules:
The shiny modules in hierarchy of calling are:
- qtlServer.R: QTL app
- mainParServer.R: main parameters
- traitServer.R: break out display of
datasetsand return oftrait_list
- traitServer.R: break out display of
- scanServer.R: QTL scan read from file
- peakServer.R: QTL peaks read from file
- mainParServer.R: main parameters
The deployable app app.R sources the file qtlSetup.R to load data files and then calls the qtlServer.R.
- qtlSetup.R: file setup
- app.R: app that calls the modules
geyser: modular concepts and construction
The geyser repo studies a simple app (Faithful) to illustrate the components of a shiny app, and how to develop more complicated apps using shiny modules. It starts with building one shiny module around this classic example, and then connects multiple modules that organize the logic (server) and app view (ui) in various ways. About a half-dozen shiny modules in geyser/R comprise the geyser R package, or library. These are of three types:
- plots: hist.R, gghist.R, ggpoint.R
- data: data.R, datasets.R data.R, datasets.R
- connections: rows.R, wrapper.R, switch.R rows.R, wrapper.R, switch.R
In addition, in the connects_modules folder, there are multiple apps that illustrate different concepts of connecting modules:
- modules over pages: appPages.R
- modules in rows and columns: appRows.R, appRowsModule.R
- reuse of one module: appTwin.R, appDupe.R, appFlip.R
- modules within modules: app.R (see deployed Geyser Demo)
For more information that puts these in context, see the Geyser Shiny Modules slide deck, as well as the 11 Dec 2024 Presentation. There are more aspects of this package, including exploration of modular apps with Quarto and Python.
foundrShiny: pragmatic code reuse driven by collaborators
The foundrShiny repo is the basis of three tools actively used by the Attie Lab
- https://connect.doit.wisc.edu/FounderCalciumStudy/
- https://connect.doit.wisc.edu/FounderDietStudy/ (requires password)
- https://connect.doit.wisc.edu/FounderLiverDietStudy/ (requires password)
This is an R package, which depends on another R package, foundr, that has the data analysis details.
Each code file in the founderShiny/R folder itself is a shiny module with a server function, UI functions, and an app function. These ~30 shiny modules are interconnected in various ways as described in the Foundr App Developer Guide in order to build the tools cited above. This was not the first, or even the second, iteration to build these tools. It took about 1.5 years to develop this system, driven and guided by interactions with Attie Lab members about use, function, and layout.
Some of those modules could be used (almost) directly for creating new shiny modules. For instance, the download.R module takes a list containing filename, plot, and table objects and arranges downloads. There are also ideas about creating and visualizing plots and tables that could prove useful. Further, there was a lot of work on figuring out how to organize input parameters across shiny modules to share inputs without duplication of code.
- app infrastructure: about.R, foundr.R, entry.R, download.R
- plots: biplot.R, dotplot.R, volcano.R
- parameters: mainPar.R, panelPar.R, plotPar.R
- panels: see panel.R or foundr.R
- trait panel: trait.R, traitNames.R, traitOrder.R, traitPairs.R, traitSolos.R, traitTable.R, corPlot.R, corTable.R
- stats panel: stats.R
- time panel: time.R, timePlot.R, timeTable.R, timeTraits.R
- contrast panel: contrast.R, contrastGroup.R, contrastPlot.R, contrastTable.R, contrastTime.R, contrastTrait.R
- non-app helpers: foundrSetup.R, foundr_helpers.R
The foundr module draws together the other modules into the app to be deployed through the panel modules contrast, stats, time, trait. The panel module is just used for testing the panels, but is not part of the foundr app; it could be modified to pull out the panel infrastructure from foundr. Several of these modules are reused. For instance, contrastPlot is used in contrastTrait and stats, timePlot is used in time and contrast, traitOrder is used in trait and time, and the parameter modules (mainPar, panelPar, plotPar) are used repeatedly. The traitNames and contrastTable modules are used multiple times in the trait and contrast panel modules, respectively. The app function of each module might use other modules (notably parameter and download modules) to test that module. Here is the hierarchy of what modules are used directly by other modules:
- foundr: mainPar, about, download, entry, contrast, stats, time, trait
- panel: mainPar, contrast, stats, time, trait
- contrast: panelPar, contrastGroup, contrastTime, contrastTable(3), contrastTrait, timePlot
- contrastGroup: contrastPlot
- contrastTable: traitOrder
- contrastTime: timeTraits
- contrastTrait: contrastPlot
- contrastPlot: plotPar, biplot, dotplot, volcano
- biplot: mainPar, panelPar, plotPar, contrastTable
- stats: panelPar, contrastPlot
- time: panelPar, timePlot, timeTable
- timeTable: timeTraits, traitOrder
- trait: panelPar, corPlot, corTable, traitNames(2), traitOrder, traitPairs, traitSolos, traitTable
The parameter modules scope inputs at different levels of the app. For instance, mainPar parameters (dataset, order, height) are common across many modules, while panelPar parameters (strains, sex, facet) are localized by panel; plotPar parameters are specific to plot modules. This took some careful thinking about how information is passed among modules.
Note that for this app, input data are not treated as global, but rather passed to the foundr module and on to each panel. Typically, static input data are subset to create reactive objects that are much smaller (focused on a particular dataset and one or a few traits) based on user input.
Having this many modules was initially confusing, but they enable a developer to concentrate app improvement on isolated parts of the app, using each module’s app function to do unit testing.
Another important aspect of this project was separating out analysis and visualization code from the reactive (shiny) code into a separate package. In fact, this app started with the foundr repo, with shiny code mixed in. The foundr v1.4 branch contains the earlier version from Summer 2024, where shiny, analysis and viz code are mixed together. The current main branch is complicated enough without shiny code, having ggplot2-based viz code and analytical computations. In addition, it has utility routines; helper routines used by foundrShiny modules but not needed for foundr routines remain in foundrShiny/R/foundr_helpers.R as mentioned earlier. Additional helpers from foundr are organized by function
- ploting
- ggplot_bestcor, ggplot_conditionContrasts, ggplot_traitPairs, ggplot_traitSolos ggplot_traitTimes
- utilities
- CCcolors, is_bestcor, keptDatatraits, subset_trait_names, timetraits, timetraitsall, unite_datatraits
- analysis
- bestcor, conditionContrasts, eigen_contrast, eigen_traits, traitPairs, traitSolos, traitTimes
- summary
- summary_bestcor, summary_conditionContrasts, summary_strainstats
qtl2shiny: localized QTL analysis and visualization
The qtl2shiny app was designed to investigate local QTL, within a small (1-4Mb) region of a peak. It performs allele-based LOD scans, SNP-based association mapping, SNP distribution pattern analysis, and mediation. This work was never published in a peer-reviewed journal, only as a set of packages in CRAN and GitHub. It is in the process of major redesign, which is documented in Shiny Module Organization.
The qtl2shiny repo was designed about a decade ago. It is currently working on a laptop with data organized in a particular fashion. However, one can view screenshots and the User’s Guide. This repo has ~25 shiny modules in qtl2shiny/R, although they do not (yet) follow the conventional naming of server and UI functions, and they do not have app functions. Nevertheless, they have many features that are being considered in current development; these could be usefully retooled for a modernized qtl2 shiny app.
The hierarchy of module calling is approximately shown in the following figure (with some missing links) and file layout here:

- Main: Dash,
- Setup: Project, Phenos, Peaks
- Haplo: Probs, SNPSetup, ScanCoef, Mediate
- Diplo: PairProbs, SNPSetup, Pattern
The above table is based on the master branch; the byandell-refactor branch is modernizing this code. Below are the branch links:
- main: dash
- setup: project, pheno, peaks
- haplo: probs, snpSetup, scanCoef, mediate
- diplo: pairProbs, snpSetup, pattern
The main module calls dash, which then invokes setup and the two primary modules, haplo and diplo, for haplotype and diplotype analyses. Each of those call multiple other modules. The app has a side panel where switches among different types of analyses and plots are performed. Some shiny technology is older–would be good to switch from shinydashboard to bslib–and more modularity is possible. Download operations are currently duplicated in multiple modules, but should be pulled out as was done for foundShiny, ideally using that same download.R module. See screenshots and guides:
Finally, qtl2shiny depends on several other R packages (some in CRAN, all in GitHub):
shiny_module: learning about reactivity
Finally, the shiny_module repo has some early learnings about reactivity. It is perhaps useful as a reference when trying to figure out why reactive code is not working. However, it is rather dated at this point.