Interactive Shiny App Templates

Turning your HTA models into tools anyone can use

R for HTA (Basics) — Workshop 2026

RRC-HTA, AIIMS Bhopal | HTAIn, DHR

What is Shiny?

Shiny is an R package that builds interactive web applications directly from R code.

No HTML, CSS, or JavaScript required.

Your HTA model (thousands of lines of R) → wrapped in a friendly UI → anyone can use in a browser.

Tip

Instead of sharing Excel with hidden formulas, you create an app where stakeholders adjust sliders and see results update instantly.

Why Shiny for HTA?

Health technology assessment is fundamentally about “What if?” questions.

  • What if treatment cost goes down?
  • What if efficacy improves?
  • What if we target a high-risk subgroup?

Shiny apps let decision-makers explore these scenarios in real time.

Bonus: Transparency and reproducibility built in. Every calculation is real R code, not hidden Excel cells.

The 4 Shiny Apps in This Workshop

  1. GDM Diagnostic Tree — 3 strategies, NMB ranking, real tornado DSA
  2. DES vs BMS — NPPA 2025 pricing, cost breakdown, break-even analysis
  3. CKD Markov Model — Trace plots, tornado DSA, PSA with CE plane + CEAC
  4. Breast Cancer PSM — Weibull survival, distribution comparison, NMB

Each is complete, ready-to-run, and easy to modify.

Try the Live Apps Now

App Link
GDM Diagnostic Tree Open
DES vs BMS Open
CKD Markov (with PSA) Open
Breast Cancer PSM Open

Try: Adjust sliders → watch ICER/NMB update. In the Markov app, click “Run PSA” for CE plane + CEAC.

Anatomy of a Shiny App

Every Shiny app has three core parts:

Code
library(DiagrammeR)

grViz("
digraph shiny_anatomy {
  graph [rankdir=LR, bgcolor='transparent', fontname='Helvetica']
  node [fontname='Helvetica', fontsize=11, style='filled,rounded', shape=box]

  user [label='User\n(Browser)', fillcolor='#76b7b2', fontcolor='white',
        shape=ellipse, width=1.5]

  subgraph cluster_ui {
    label='UI (Frontend)'
    style=filled
    fillcolor='#d4e6f1'

    inputs [label='Input Widgets\n(sliders, dropdowns,\ntext boxes)',
            fillcolor='#4e79a7', fontcolor='white']
    outputs [label='Output Displays\n(plots, tables,\nresults)',
             fillcolor='#4e79a7', fontcolor='white']
  }

  subgraph cluster_server {
    label='Server (R Logic)'
    style=filled
    fillcolor='#d5f5e3'

    model [label='HTA Model\n(tree, Markov,\nPSM, etc)',
           fillcolor='#59a14f', fontcolor='white']
    render [label='Render Outputs\n(create plots,\ncompute ICER)',
            fillcolor='#59a14f', fontcolor='white']
  }

  user -> inputs [label='Adjusts\nsliders']
  inputs -> model [label='Send\nparameters']
  model -> render [label='Calculate']
  render -> outputs [label='Update']
  outputs -> user [label='Display\nresults']
}
")
Figure 1: How Shiny connects the user to your HTA model

The Three Parts: UI, Server, shinyApp()

library(shiny)

# 1. DEFINE USER INTERFACE
ui <- fluidPage(
  titlePanel("My First HTA App"),
  sidebarLayout(
    sidebarPanel(
      sliderInput("cohort_size", "Cohort Size:",
                  min = 100, max = 10000, value = 1000)
    ),
    mainPanel(
      textOutput("result")
    )
  )
)

# 2. DEFINE SERVER LOGIC
server <- function(input, output) {
  output$result <- renderText({
    total_cost <- input$cohort_size * 50000
    paste("Total cost for cohort: ₹", format(total_cost, big.mark=","))
  })
}

# 3. COMBINE UI AND SERVER
# shinyApp(ui = ui, server = server)
# (Don't actually run here, just show the pattern)

Key idea: When user moves slider, input$cohort_size updates → server re-runs → output$result updates → browser shows new result.

Running a Shiny App Locally

Open an app’s app.R file in RStudio:

Option 1: Click the “Run App” button in the script pane

Option 2: Type in console:

shiny::runApp("path/to/app.R")

The app launches in your default browser. Press Escape or close the browser to stop.

Running a Shiny App From This Workshop

# Navigate to the app directory
cd day3/session10-shiny-apps/gdm-decision-tree-app

# In RStudio, open app.R and click "Run App"
# OR in R console:
shiny::runApp()

Explore: - Adjust sliders (probabilities, costs) - See plots and tables update in real time - Understand how each parameter affects the ICER

Key UI Widgets (Inputs)

# SLIDER: continuous parameter
sliderInput("discount_rate", "Discount Rate (%):",
            min = 0, max = 10, value = 3, step = 0.1)

# NUMERIC INPUT: single value
numericInput("cohort_size", "Cohort Size:",
             value = 1000, min = 1)

# DROPDOWN: choose one option
selectInput("distribution", "Survival Distribution:",
            choices = c("Weibull", "Exponential", "Log-logistic"),
            selected = "Weibull")

# RADIO BUTTONS: choose one (visible)
radioButtons("currency", "Currency:",
             choices = c("INR" = "inr", "USD" = "usd"),
             selected = "inr")

# CHECKBOX: toggle on/off
checkboxInput("include_psa", "Include PSA?", value = FALSE)

Key Server Functions (Outputs)

# RENDER TEXT
output$summary <- renderText({
  paste("ICER: ₹", format(round(icer()), big.mark=","), "/QALY")
})

# RENDER PLOT
output$ce_plane <- renderPlot({
  ggplot(ce_data, aes(x = inc_qaly, y = inc_cost)) +
    geom_point(alpha = 0.5) +
    geom_abline(intercept = 0, slope = input$wtp_threshold)
    # Note: input$wtp_threshold updates automatically
})

# RENDER TABLE
output$results_table <- renderTable({
  data.frame(
    Strategy = c("Control", "Intervention"),
    Cost = c(cost_control, cost_intervention),
    QALYs = c(qaly_control, qaly_intervention)
  )
})

Reactivity: When input$wtp_threshold changes, renderPlot() re-runs automatically.

Example: Simple CKD Markov App

# Sidebar inputs
sidebarPanel(
  titlePanel("CKD Markov Model"),
  sliderInput("baseline_cost", "Annual cost (Stage 1):",
              min = 5000, max = 50000, value = 20000),
  sliderInput("treatment_effect", "Treatment effect (%):",
              min = 0, max = 50, value = 20)
)

# Main panel outputs
mainPanel(
  tabsetPanel(
    tabPanel("Results",
             textOutput("icer_summary"),
             tableOutput("cost_table")),
    tabPanel("Sensitivity Analysis",
             plotOutput("tornado_plot")),
    tabPanel("Cohort Trace",
             plotOutput("state_occupancy"))
  )
)

This structure is identical across all four workshop apps. Only the HTA model logic changes.

Modifying the Apps

The apps are designed to be adapted:

Want to add a parameter? 1. Add a sliderInput() in the UI 2. Use input$parameter_name in the server calculation 3. Re-run the app

Want to change a plot theme? → Edit the ggplot() code directly

Want to add a new output? → Add a renderPlot() or renderTable() in the server, then reference it in the UI

Note

You don’t need to understand all the Shiny internals. Just focus on: inputs, model logic, outputs.

Sharing Shiny Apps

Option 1: shinyapps.io (Free) - Cloud hosting (Posit/RStudio account) - Deploy with one click: rsconnect::deployApp() - Others access via URL in browser

Option 2: Shiny Server (Self-hosted) - Host on your organization’s server - Control who has access - More control, more setup

Option 3: Share the app.R file - Email or GitHub - Others run locally with shiny::runApp() - Simplest; no server needed

For this workshop: Start with Option 3 (share app.R file).

Modifying Apps for Your Organization

Change parameter defaults and ranges to match your data:

# BEFORE: Generic defaults
sliderInput("treatment_cost", "Annual treatment cost:",
            min = 10000, max = 500000, value = 100000)

# AFTER: Your organization's estimates
sliderInput("treatment_cost", "Annual trastuzumab cost (INR):",
            min = 350000, max = 500000,
            value = 420000,  # Your base case
            step = 10000)

This is your model. Own it. Change assumptions to reflect your beliefs.

Don’t Build from Scratch

Important

This session is NOT about learning to code Shiny from the ground up.

You are learning to: 1. Understand how provided apps work 2. Identify which template matches your problem 3. Modify parameters and ranges 4. Add/remove outputs (plots, tables)

If you want to dive deeper later: Read Mastering Shiny by Hadley Wickham.

For now: Use the templates.

The Power of Interactive Models

Without Shiny: - Share a PDF report - “If you want to change an assumption, email me” - Decision-makers are passive

With Shiny: - Deploy an interactive app - Decision-makers explore scenarios themselves - Trust increases; decisions are informed

Tip

By publishing your Shiny app, you are democratizing health technology assessment.

Summary

✓ Shiny wraps your HTA model in a user-friendly web interface

Three-part structure: UI (inputs) → Server (logic) → shinyApp()

No HTML/CSS/JavaScript needed — it’s all R

✓ Easy to modify: change sliders, add plots, adjust assumptions

✓ Easy to share: email app.R or deploy to cloud

Next: Wrap-up everything you’ve learned in 3 days (Session 11)

References

  • Wickham H. (2021). Mastering Shiny. O’Reilly Media. [Free online at mastering-shiny.org]
  • Shiny Official Documentation: https://shiny.rstudio.com/
  • DARTH Group Shiny examples on GitHub