Introducing {shinyfa}: A Developer Experience Boost for Shiny Apps

shinyfa hex sticker

I’m thrilled to share a new R package I’ve been working on: {shinyfa} — short for Shiny File Analysis. This package is designed to enhance the developer experience (DX) for folks working with large, complex Shiny applications.

🔗 GitHub: dalyanalytics/shinyfa

Why {shinyfa}?

If you’ve ever joined a project with a sprawling Shiny codebase—hundreds of files, deeply nested folders, and server logic scattered across modules—you know how daunting it can be just to figure out where to start.

For consultants, new hires, or contributors onboarding to a project, {shinyfa} provides an immediate overview of what the app is doing, and where.

What Does It Do?

{shinyfa} inspects a given Shiny app directory and extracts key file-level information, including:

  • render*() functions (like renderPlot, renderTable, renderUI, etc.)

  • reactive(), observe(), observeEvent() and other reactive components

  • Input bindings like input$ used within the app

It then returns a structured, tidy data.frame (or tibble) summarizing where these elements appear in your app. This gives you a searchable, sortable, filterable bird’s-eye view of the logic powering your application.

Who Is This For?

This package is especially useful for:

  • 🌅 Consultants jumping into existing client projects

  • 🧭 Internal dev teams who want a clearer map of how their app works

  • 🌱 New team members onboarding to a complex Shiny product

  • 👩‍💻 Tech leads looking to identify opportunities for refactoring or optimization

Even if your app is nicely modularized, {shinyfa} can still save time by giving you a summary of reactive logic at a glance.

How to Get Started

Install the package from GitHub using {pak}:

# install.packages("pak")
pak::pak("dalyanalytics/shinyfa")

Then run it on a Shiny app directory:

library(shinyfa)
library(dplyr)  

file_path_df <- list.files("SHINY-SERVER-DIRECTORY", 
                           pattern = "\\.R$", full.names = TRUE)

file_analysis <- data.frame()  # Initialize an empty dataframe

for (file in file_path_df) {
  shiny_analysis <- analyze_shiny_reactivity(file_path = file)
  
  # Skip if NULL (empty file or only `source()` calls)
  if (is.null(shiny_analysis)) next
  
  # Add filename column
  shiny_analysis$file_name <- basename(file)
  
  # Bind results
  file_analysis <- bind_rows(file_analysis, shiny_analysis)
}

print(file_analysis)

Help Improve {shinyfa}!

This is an early-stage, in-progress package—and I’d love your help making it better. If you try it out and have suggestions, please:

Next
Next

Interactive Data Storytelling for Conservation: A Friendly Guide to Making Your Impact Unmistakable