Skip to main content

Shiny β€” NHS Quickstart

πŸ§ͺ R Β· Interactive dashboards Β· SQL Server/Parquet Β· NHS-friendly hosting
Why Shiny in the NHS

If your team already uses R for stats and reporting, Shiny converts existing analysis into interactive apps with minimal extra code. It’s ideal for internal dashboards where you want auditable R scripts, quick iteration, and secure, behind-SSO access.

Great for: Clinician-Researcher Β· BI Analyst (R-heavy) Β· Data Scientist.


βš™οΈ 10-minute install​

In the R console:

install.packages(c("shiny","DBI","odbc","ggplot2","dotenv","readr","dplyr","arrow"))
  • For SQL Server access, install the Microsoft ODBC Driver 18 for SQL Server for your OS.
  • Keep secrets out of code; use .Renviron or the dotenv package locally and a secret store in production.

πŸš€ β€œHello NHS” Shiny app (choose a data source)​

Folder layout

shiny-nhs/
.env
app.R
data/
kpi.parquet # or kpi.csv

.env (local only β€” never commit)

DATA_PATH=data/kpi.parquet

app.R

library(shiny)
library(ggplot2)
library(dplyr)
library(dotenv)
library(arrow) # for Parquet

load_dot_env()
data_path <- Sys.getenv("DATA_PATH", unset = "data/kpi.parquet")

df <- arrow::read_parquet(data_path)

ui <- fluidPage(
titlePanel("NHS KPI Dashboard (Shiny)"),
selectInput("top_n", "Top N practices", choices = c(5,10,20), selected = 10),
plotOutput("barPlot")
)

server <- function(input, output, session) {
output$barPlot <- renderPlot({
df |>
arrange(desc(total_appointments)) |>
head(as.numeric(input$top_n)) |>
ggplot(aes(x = reorder(practice_id, total_appointments),
y = total_appointments)) +
geom_col(fill = "#005EB8") +
coord_flip() +
labs(x = "Practice", y = "Appointments (30d)",
title = "Appointments by Practice") +
theme_minimal(base_size = 12)
})
}

shinyApp(ui, server)

Run in R:

shiny::runApp("shiny-nhs")

🧰 NHS-ready add-ons​

  • Branding: apply NHS.UK palette via bslib or use nhsukdown.
  • Auth/SSO: place Shiny behind a reverse proxy (IIS/NGINX) integrated with Entra ID / NHS Login.
  • Caching: cache expensive queries with memoise or precompute to Parquet on a schedule.
  • Validation: add a lightweight checks page (row counts, freshness) rendered from a validation script.
  • Containerisation: package with Docker for consistent deploys (then run on App Runner / Azure Container Apps).

🚒 Deploy options​

  • Posit Connect (formerly RStudio Connect) β€” easiest publishing, SSO integration options.
  • Shiny Server Open Source β€” single box; put behind SSO at the edge.
  • Docker + Cloud β€” containerise and deploy to AWS App Runner or Azure Container Apps; serve over HTTPS; restrict by IP/SSO.
  • Intranet β€” host on a VM, fronted by IIS/NGINX with basic auth or integrated auth.

Document owner, refresh cadence, and datasets in the app README.


πŸ”’ IG & safety checklist​

  • Use synthetic/de-identified sample data for examples.
  • Keep secrets in environment variables; never commit .Renviron/.env.
  • Apply suppression for small numbers before plotting.
  • Log access via reverse proxy and keep an audit trail of releases.
  • Minimise live DB access; prefer read-only extracts for dashboards.

πŸ“ Measuring impact​

  • Latency: data refresh β†’ app reflects change.
  • Stability: uptime and error rate.
  • Quality: validation pass rate (row counts, freshness checks).
  • Adoption: weekly active users; stakeholder satisfaction.

πŸ”— See also​

See also: R Β· SQL Β· DuckDB Β· Dash Β· Docker Β· AWS Β· Azure Β· Secrets & .env

What’s next?

You’ve completed the Learn β€” Shiny stage. Keep momentum: