Modular Shiny Applications

Design, Development, Reactivity, and Scale

Brian Yandell (byandell.github.io)

2026-06-18

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 python vs R implementations in the Shiny Geyser App.

Key Repositories Overview

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

  • helperApps: General-purpose modules designed for direct reuse in other Shiny apps.
  • qtlApp: Hierarchical module design for QTL visualization at scale.
  • geyser: Learning sandbox showcasing simple module connection patterns.
  • foundrShiny: Multi-portal package driven by collaborator layout requirements.
  • qtl2shiny: Multi-module app for local genomic analysis.
  • shiny_module: Interactive repository for testing early reactivity concepts.

Case Studies: qtlApp & geyser

qtlApp: QTL Visualization at Scale

Designed for QTL analysis at scale, organized as an R package with separate shiny modules.

Calling Hierarchy

  • qtlServer.R: Top-level server logic.
    • mainParServer.R: Shared parameters.
      • traitServer.R: Dataset display and returns trait_list.
    • scanServer.R: Reads scans from files.
    • peakServer.R: Reads peaks from files.

Infrastructure & Setup

  • qtlSetup.R: Standard initialization script to locate and load files into the environment.
  • app.R: Deployable entry-point script calling the modules.
  • Helper Scripts: Decouple visualization (QTL_plot_visualizer.R) and analysis (trait_scan.R, peak_finder.R).

geyser: Modular Concepts

Uses the standard Old Faithful geyser app to illustrate module composition and connection logic:

Module Library (geyser/R)

  • Plots:
    • hist.R / gghist.R: Base & ggplot histogram.
    • ggpoint.R: Scatterplot modules.
  • Data:
    • data.R / datasets.R: Data loader modules.
  • Connections:
    • rows.R / wrapper.R / switch.R: Connect controllers.

Connection Layout Apps

  • Over Pages: appPages.R.
  • Rows & Columns: appRows.R / appRowsModule.R.
  • Twin Reuse: appTwin.R / appDupe.R / appFlip.R.
  • Nested: app.R (see deployed Geyser Demo).

Tip

See the Geyser Shiny Modules Slides and 11 Dec 2024 Presentation for walk-throughs.

foundrShiny: Collaborator-Driven Architecture

foundrShiny: Lab Portals

Powers three active research portals for the Attie Lab:

  • Study Portals: Founder Calcium Study, Founder Diet Study, and Founder Liver Diet Study.
  • Dependency: Leverages the foundr data analysis package.
  • Scale: Over 30 interconnected modules (e.g. download.R for exports, volcano.R for plots).
  • Iteration: Evolved over 1.5 years through collaboration on layouts, panel structures, and feature additions.

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.
  • R Helpers: Analysis helpers are strictly organized by category (e.g. plotting, summary, calculation, and utilities).

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:

Server Refactoring Tree

The modernization branch (byandell-refactor) reorganizes server calls: - mainServer.R - dashServer.R - setupServer.R: project, pheno, peaks. - haploServer.R: probs, snpSetup, scanCoef, mediate. - diploServer.R: pairProbs, snpSetup, pattern.

Modernization Goals

  • Dashboard Upgrades: Transition older screens from shinydashboard layouts to modern, responsive bslib dashboards.
  • Consolidation: Eliminate duplicate download routines using the unified download.R module.

Dependencies & Reactive Debugging

qtl2 Package Stack

Sandbox Learning

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

Version Notes

The original qtl2shiny repository contains legacy module syntax. Active retooling is recommended to migrate functions into compliant server/UI wrappers for easier testing.