Skip to main content
v2026.11,986 entries · CC-BY 4.0

Sankey Diagram in R: networkD3 & ggalluvial

Build Sankey diagrams in R using networkD3 and ggalluvial. Format zero-indexed node-link data, export publication figures, and resolve common layout errors.

Written and maintained by CASRAI Editorial Board

Published

On this page

What it is used for in research

A Sankey diagram is a specialized directed flow visualization where the width of connecting bands is proportional to the quantity of flow between categorical states or process stages. In academic research, investigators use Sankey diagrams to depict multi-stage transitions, cohort attrition, energy or mass balances, and categorical sequence data. Unlike standard static flowcharts, Sankey diagrams preserve quantitative proportions across every junction, enabling readers to identify primary conduits, bottlenecks, and loss pathways across complex experimental or observational workflows.

Primary research applications across scientific disciplines include:

  • Clinical Trial & Epidemiological Cohorts: Visualizing participant progress through clinical protocols, tracking attrition from initial screening and randomization through intervention arms to completed follow-up or loss to follow-up.
  • Life Cycle Assessment (LCA) & Energy Systems: Mapping raw material extraction, transformation, transmission losses, and final end-use consumption in environmental engineering and circular economy studies.
  • Bioinformatics & Systems Biology: Tracing gene expression classifications, microbiome taxonomic shifts across treatment timepoints, or multi-omics data integration flows.
  • Education & Career Trajectories: Analyzing longitudinal survey data, student retention across degree milestones, and interdisciplinary researcher mobility patterns.

While researchers working in Python often rely on Plotly or Matplotlib (see our guide to Sankey diagrams in Python), the R statistical environment provides two specialized ecosystems. For interactive exploratory dashboards and web-based supplementary materials, the networkD3 package produces dynamic D3.js JavaScript widgets. For static, high-resolution vector figures destined for print manuscripts, the ggalluvial package extends the Grammar of Graphics to generate publication-grade scientific figures directly within ggplot2.

Install or access (official source only)

Both primary packages are available directly from the Comprehensive R Archive Network (CRAN) and can be installed into an active R session (version 3.0.0 or higher) using the standard CRAN package manager. For comprehensive package setup rules, review our tutorial on how to install libraries in R.

Run the following command in the R console to install the packages from an official CRAN mirror:

# Install official CRAN packages
install.packages("networkD3")
install.packages("ggalluvial")
install.packages("dplyr")

To access the installed libraries and verify version compatibility, load them into your script or R Markdown/Quarto document:

# Load libraries into environment
library(networkD3)
library(ggalluvial)
library(dplyr)

# Verify loaded versions
packageVersion("networkD3")  # Current CRAN release: 0.4
packageVersion("ggalluvial") # Current CRAN release: 0.12.5

Step-by-step task (numbered, with menu names or commands exactly as in the current docs)

Building a Sankey diagram in R involves preparing research flow data, mapping entities to package-specific structures, and rendering the final graphic. The steps below detail how to construct both an interactive D3 diagram and a static manuscript-ready vector figure.

Step 1: Structure the Raw Research Data

Real-world research observations typically start as tabular data frames in R recording categorical patient or specimen states across consecutive experimental stages. Create a representative clinical cohort dataset recording participant progression across three study milestones:

# Sample research cohort: Screening -> Intervention -> 12-Month Outcome
cohort_data <- data.frame(
  Screening    = c("Eligible", "Eligible", "Eligible", "Ineligible"),
  Intervention = c("Treatment Arm", "Treatment Arm", "Control Arm", "Excluded"),
  Outcome      = c("Remission", "Relapse", "Remission", "None"),
  Count        = c(45, 15, 30, 20)
)

Step 2: Method A — Build an Interactive D3 Diagram with networkD3

The networkD3::sankeyNetwork() function requires two distinct data frames: a Nodes table containing unique entity labels, and a Links table specifying connection pairs with zero-indexed integer IDs and numeric flow weights.

  1. Derive Pairwise Aggregations: Transform multi-stage cohort data into pairwise source-to-target links:
    # Stage 1 to Stage 2 links
    link1 <- cohort_data %>%
      group_by(source = Screening, target = Intervention) %>%
      summarise(value = sum(Count), .groups = "drop")
    
    # Stage 2 to Stage 3 links
    link2 <- cohort_data %>%
      filter(Intervention != "Excluded") %>%
      group_by(source = Intervention, target = Outcome) %>%
      summarise(value = sum(Count), .groups = "drop")
    
    # Combine all pairwise transitions
    links_raw <- bind_rows(link1, link2)
    
  2. Construct Node Lookup with Zero-Indexed Mapping: Because JavaScript arrays count from 0 while R vectors count from 1, failure to subtract 1 will cause node lookup errors or omit the final node:
    # Create unique node list
    nodes <- data.frame(
      name = unique(c(links_raw$source, links_raw$target)),
      stringsAsFactors = FALSE
    )
    
    # Map string labels to zero-indexed integer identifiers
    links <- links_raw %>%
      mutate(
        IDsource = match(source, nodes$name) - 1,
        IDtarget = match(target, nodes$name) - 1
      )
    
  3. Execute sankeyNetwork: Render the interactive widget with explicit styling and unit labels:
    # Generate interactive Sankey diagram
    sankey_widget <- sankeyNetwork(
      Links = links,
      Nodes = nodes,
      Source = "IDsource",
      Target = "IDtarget",
      Value = "value",
      NodeID = "name",
      units = "Patients",
      fontSize = 12,
      fontFamily = "Arial",
      nodeWidth = 20,
      nodePadding = 12,
      sinksRight = FALSE
    )
    
    # View in RStudio Viewer or web browser
    sankey_widget
    
  4. Export Standalone HTML: Save the interactive widget for web supplements using saveNetwork():
    # Export to standalone HTML file
    saveNetwork(sankey_widget, file = "figure1_interactive.html", selfcontained = TRUE)
    

Step 3: Method B — Build a Static Vector Diagram with ggalluvial

For peer-reviewed journal submission requiring high-resolution vector graphics (PDF/EPS), use ggalluvial to preserve native ggplot2 thematic control.

  1. Specify Alluvial Coordinates: Map multi-stage variables directly to categorical axis aesthetics inside aes():
    library(ggplot2)
    library(ggalluvial)
    
    # Construct static alluvial/Sankey plot
    p_static <- ggplot(
      data = cohort_data,
      aes(y = Count, axis1 = Screening, axis2 = Intervention, axis3 = Outcome)
    ) +
      geom_alluvium(aes(fill = Intervention), width = 1/4, knot.pos = 0.4, alpha = 0.7) +
      geom_stratum(width = 1/4, fill = "grey92", color = "grey30") +
      geom_text(stat = "stratum", aes(label = after_stat(stratum)), size = 3.2, fontface = "bold") +
      scale_x_discrete(limits = c("Screening", "Intervention", "Outcome"), expand = c(0.12, 0.05)) +
      scale_fill_brewer(palette = "Set2") +
      theme_minimal(base_size = 11) +
      theme(
        legend.position = "bottom",
        panel.grid.major.x = element_blank(),
        panel.grid.minor = element_blank(),
        axis.text.y = element_text(color = "black")
      ) +
      labs(
        title = "Participant Flow Across Study Milestones",
        y = "Number of Enrolled Participants",
        x = "Trial Stage",
        fill = "Treatment Allocation"
      )
    
    print(p_static)
    
  2. Export Vector Figures for Manuscript Submission: Use ggsave() to output publication-compliant PDF and TIFF files:
    # Export publication-ready vector PDF (for peer review)
    ggsave("Figure1_Cohort_Flow.pdf", plot = p_static, width = 7.5, height = 5.0, units = "in")
    
    # Export high-resolution raster TIFF (300 DPI)
    ggsave("Figure1_Cohort_Flow.tiff", plot = p_static, width = 7.5, height = 5.0, units = "in", dpi = 300)
    

Output and how to read it

A Sankey diagram displays flows through three interconnected visual elements:

  • Nodes (Strata): Vertical rectangular blocks representing discrete categories, experimental conditions, or milestone stages. The vertical height of each stratum represents the cumulative total volume or sample size passing through that condition.
  • Links (Alluvia / Ribbons): Curved bands spanning between nodes. The thickness of each link corresponds strictly to the magnitude of the transition value ($y$-aesthetic or Value parameter). A wider ribbon indicates a greater proportion of items transitioning between those two states.
  • Flow Conservation: In balanced systems (e.g., energy or financial budgets), the total incoming ribbon width at an intermediate node equals the total outgoing ribbon width. In cohort or attrition workflows, differences between incoming and outgoing widths visually highlight dropouts, treatment non-compliance, or system loss.

When reviewing interactive networkD3 output, hovering over any ribbon activates an inspection tooltip displaying the source node, target node, and exact numeric magnitude formatted with the user-defined units string. Clicking and dragging vertical node bars allows the researcher to manually optimize vertical spacing and resolve overlapping link trajectories. In static ggalluvial output, color shading tracks specific cohorts across all axes simultaneously, allowing readers to trace sub-populations from inception to study conclusion.

Troubleshooting

When constructing flow diagrams in R, users encounter recurring configuration challenges stemming from data indexing, topological constraints, or graphic parameters. The table below provides verified solutions to primary failure modes:

Error or Symptom Underlying Technical Cause Verified Corrective Action
Error in if (links$source[i] == links$target[i]) or layout hangs Circular flow or feedback loop in networkD3. D3’s iterative relaxation layout algorithm requires a strict Directed Acyclic Graph (DAG). Remove self-referential transitions or disambiguate repeated categories across stages by appending stage suffixes (e.g., Remission_T1 and Remission_T2).
Blank diagram or missing terminal nodes in networkD3 1-based indexing passed to JavaScript. D3 requires 0-indexed node IDs (ranging from $0$ to $N-1$). Subtract 1 when mapping character labels to node indices: IDsource = match(source, nodes$name) - 1.
Overlapping node labels or cramped strata in dense multi-stage plots Insufficient vertical whitespace between closely spaced categorical strata. Increase nodePadding in sankeyNetwork() (e.g., set to 15 or 20). In ggalluvial, adjust stratum width to 1/4 or reduce label font size in geom_text().
Missing ribbons or broken flows across intermediate axes in ggalluvial Missing values (NA) in intermediate stages causing cohort tracking fragmentation. Explicitly impute missing milestone steps as a distinct category (e.g., “Discontinued” or “Lost to Follow-up”) prior to plotting, or set na.rm = FALSE.

Free alternatives (table)

Several free and open-source packages in the R and scientific computing ecosystem support flow and alluvial visualizations. The matrix below compares functional characteristics to assist researchers in selecting the appropriate tool:

Tool / Package Rendering Engine Primary Data Structure Output Format Best Research Use Case CRAN Status
ggalluvial ggplot2 / Grid graphics Wide multi-axis or long lode data frames Vector PDF, SVG, EPS, PNG Academic manuscripts, grant proposals, multi-panel figures Official CRAN package
networkD3 D3.js JavaScript Pairwise 0-indexed Node-Link data frames Standalone interactive HTML Web reports, Shiny applications, online supplementary data Official CRAN package
plotly (R) Plotly.js / WebGL Zero-indexed source, target, and value arrays Interactive HTML, SVG/PNG via Kaleido Complex multi-attribute exploratory web applications Official CRAN package
ggsankey ggplot2 / Grid graphics Long format via make_long() helper Vector PDF, SVG, PNG Custom curved flow ribbons with distinct start/end widths GitHub package
Python Plotly Plotly.js / JavaScript Dictionary structures and Pandas DataFrames Interactive HTML, static vector PDF/SVG Integrated Python data pipelines and Jupyter notebooks (see Plotly vs Matplotlib) PyPI / Conda

FAQ

Can I create a Sankey diagram using base R graphics without external packages?

Base R graphics (such as plot(), lines(), or polygon()) do not include native primitives for computing cubic spline ribbons or automatic iterative layout relaxation. While it is theoretically possible to manually calculate polynomial coordinates and draw filled polygons, doing so requires hundreds of lines of custom mathematical code. Using CRAN-verified packages such as ggalluvial or networkD3 is standard scientific practice.

How do I convert an interactive networkD3 Sankey diagram into a static publication figure?

To export a networkD3 widget as a high-resolution static image, save the widget to an HTML file using saveNetwork(widget, "sankey.html") and then render it to an image format using the CRAN package webshot2 via webshot2::webshot("sankey.html", "sankey.png", vwidth = 1200, vheight = 800). However, for peer-reviewed journal submission requiring vector PDF or EPS formats, generating the plot directly with ggalluvial and exporting via ggsave() is strongly recommended.

What is the difference between a Sankey diagram and an alluvial diagram in R?

In data visualization terminology, a classic Sankey diagram visualizes continuous flows (such as energy, fluid, or money) across arbitrary network topologies with node-level flow conservation. An alluvial plot is a specialized structured form of Sankey diagram designed for repeated-measures categorical data, where categorical dimensions are arranged along discrete parallel vertical axes and ribbons track cohorts transitioning from axis to axis. In R practice, ggalluvial specializes in axis-based categorical cohorts, while networkD3 supports arbitrary directed network flow structures.

Why does my networkD3 Sankey diagram render as a completely blank box?

A blank output window almost always indicates an indexing discrepancy between R and JavaScript. If your IDsource or IDtarget columns contain 1-based indices (or character strings instead of integers), JavaScript fails silently to bind links to nodes. Always verify that your link identifiers are integers starting at 0, and ensure no NA values or missing node names exist in the link list.

Sources (official docs)

  • Brunson, J. C., & Read, Q. D. (2023). ggalluvial: Alluvial Plots in ‘ggplot2’. R package version 0.12.5. Comprehensive R Archive Network (CRAN). https://cran.r-project.org/package=ggalluvial (Accessed 2026-11-10).
  • Gandrud, C., Allaire, J. J., Russell, K., & Yetman, C. J. (2020). networkD3: D3 JavaScript Network Graphs from R. R package version 0.4. Comprehensive R Archive Network (CRAN). https://cran.r-project.org/package=networkD3 (Accessed 2026-11-10).
  • Plotly Technologies Inc. (2026). Sankey Diagrams in R: Open Source Graphing Library. Plotly R Documentation. https://plotly.com/r/sankey-diagram/ (Accessed 2026-11-10).
  • The R Foundation for Statistical Computing. (2026). R: A Language and Environment for Statistical Computing. Comprehensive R Archive Network. https://www.r-project.org/ (Accessed 2026-11-10).

Follow CASRAI

Research-administration guidance, standards updates and independent tool reviews.

Ask CASRAI · free to try

Ask about Sankey Diagram in R: networkD3 & ggalluvial

Ask your first 2 questions free below. Subscribers get 150 a day for $29 a month.

An AI assistant specialized in research administration. It cites the sources behind every answer, labels web answers and says when it can't answer.

Answers draw on CASRAI's guides and dictionary plus the federal and funder documents we index: Federal Register, Grants.gov, Regulations.gov and UKRI.

Works on this site and inside Claude, Cursor and the AI tools you already use.

Everything CASRAI publishes — this page, the dictionary, the guides and the news — stays free to read, with no account and no card.

Referenced across the research world

University of Cambridge logoColumbia University logoCrossref logoUniversity of Edinburgh logoHarvard University logoUniversity of Oxford logoPrinceton University logoStanford School of Medicine logoUniversity College London logoORCID logoUniversity of Cambridge logoColumbia University logoCrossref logoUniversity of Edinburgh logoHarvard University logoUniversity of Oxford logoPrinceton University logoStanford School of Medicine logoUniversity College London logoORCID logo
  • University of Cambridge logo
  • Columbia University logo
  • Crossref logo
  • University of Edinburgh logo
  • Harvard University logo
  • University of Oxford logo
  • Princeton University logo
  • Stanford School of Medicine logo
  • University College London logo
  • ORCID logo

View CASRAI adoption →