Modular Shiny Applications

Design, Development, Reactivity, and Scale

Brian Yandell (byandell.github.io)

2026-07-30

Reactive Complexity & Design

The Challenge of Large Shiny Apps

Why Modularize?

  • Shiny apps are highly interactive, but large apps can become complicated and frustrating to debug.
  • Single apps longer than 500 lines often suffer from subtle paradoxes caused by reactive elements.
  • Modularization breaks code into self-contained components (like functions) to allow piece-by-piece testing and reuse.

Core References

  • For multi-language comparisons, see R and Python implementations in Shiny Geyser App.

Key Repositories Overview

Several packages and applications illustrate an evolving design pattern aimed at improving usability, performance, and debuggability:

  • geyser: Learning sandbox showcasing R & Python module connection patterns and Shinylive demos.
  • foundrShiny: Multi-portal package driven by collaborator layout requirements.
  • qtl2shiny: Multi-module app for local genomic analysis.
  • helperApps: General-purpose modules designed for direct reuse in other Shiny apps.
  • shiny_module: Sandbox for testing early reactivity concepts.

Case Study: geyser

geyser: Modular Concepts

Uses the standard Old Faithful geyser app to illustrate module composition, multi-language support, and WebAssembly deployment:

Published Site & Demos

  • geyser Site: Documentation, developer guides, and slide decks.
  • Shinylive Demos: Interactive browser execution without a server.
  • Dual Support: Modules built for both R and Python (shiny.express & shiny.ui).

Modular Progression

  • 1. Old Faithful Monolith: Single-file app.
  • 2. UI & Server Split: Function modularization.
  • 3. Single Module: Server/UI module encapsulation.
  • 4. Connected Modules: Modules over pages, grid rows/cols, twin reuse, and nested app layouts.

Tip

See the R Shiny Modules Slide Deck and Python Shiny Modules Slide Deck for detailed walkthroughs.

foundrShiny: Collaborator-Driven Architecture

foundrShiny: Lab Portals

Powers active research portals for the Attie Lab founder mouse studies:

Parameter Scoping & Hierarchy

Input Scoping Levels

To prevent code duplication, inputs are scoped at three distinct levels: 1. mainPar: Shared parameters (e.g. dataset choice, height) globally available. 2. panelPar: Localized settings for a specific dashboard panel (e.g. strains, sex, facet overlays). 3. plotPar: Specific options limited to visualization plots.

Calling Hierarchy

  • foundr: mainPar, about, download, entry, contrast, stats, time, trait
    • contrast: panelPar, contrastGroup, contrastTime, contrastTable, contrastTrait, timePlot
      • contrastGroup: contrastPlot
      • contrastTrait: contrastPlot
    • stats: panelPar, contrastPlot
    • time: panelPar, timePlot, timeTable
    • trait: panelPar, corPlot, corTable, traitNames, traitOrder, traitPairs, traitSolos, traitTable

Reactive Data Flow

Inputs are passed directly into the foundr module. The application avoids global variables; instead, static inputs are filtered into small reactive subsets based on user panels.

Decoupling Reactivity from Analysis

A key architectural shift was completely separating data analysis from the reactive Shiny interface.

Legacy Layout

  • Mixed Code: Early versions (such as the foundr v1.4 branch) mixed reactivity, analysis calculations, and plotting configurations in single files.
  • Risk: Hard to debug, poor test coverage, and fragile scaling.

Decoupled Layout

  • Analysis Package (foundr): Houses clean, non-reactive R code (ggplot structures, mathematical steps).
  • Shiny Package (foundrShiny): Manages UI wrappers, reactivity, session states, and client connections.

qtl2shiny: Scaling & Modernization

qtl2shiny: Local QTL Analytics

Designed for local QTL investigations within a 1-4Mb peak region, including LOD scans, SNP association, and mediation:

Analysis Panel Architecture

Organized across 6 dedicated analysis panels: - hotspotApp: Genome-wide hotspot count scans. - scanApp: LOD scans & founder allele effects. - patternApp: Strain distribution pattern grouping. - genoApp: Genotype probabilities & gene region tracks. - scatterApp: Bivariate mediator scatter plots. - mediateApp: Candidate gene mediation scans.

Modernization Goals

  • bslib Dashboards: Transition older screens from shinydashboard to modern, responsive bslib tabs.
  • Developer Guide: qtl2shiny Articles.
  • Unified Downloads: Integrated downr export widgets across panels.

Dependencies & Reactive Debugging

qtl2 Package Stack

Sandbox Learning

  • shiny_module package:
    • Sandbox designed to understand core reactivity rules and event sequences.
    • Useful reference for tracing reactive logs and resolving unresponsive inputs.

Developer Guide Reference

Explore the full architecture and module calling trees in the published qtl2shiny Module Architecture Article.