Gantt Chart with Dependencies — ggplot2

A Gantt chart that visualizes project schedules with task dependencies and groupings. Beyond displaying task timelines, this chart shows relationships between tasks using connector arrows, indicating which tasks must complete before others can begin. Tasks can be organized into groups (phases or work packages) with aggregate timeline bars showing the span of each group. This visualization is essential for understanding critical paths and scheduling constraints in complex projects.

Gantt Chart with Dependencies rendered with ggplot2

Renders

R source (ggplot2)

#' anyplot.ai
#' gantt-dependencies: Gantt Chart with Dependencies
#' Library: ggplot2 3.5.1 | R 4.4.1
#' Quality: 88/100 | Created: 2026-06-02

library(ggplot2)
library(ragg)

# --- Theme tokens ---
THEME       <- Sys.getenv("ANYPLOT_THEME", "light")
PAGE_BG     <- if (THEME == "light") "#FAF8F1" else "#1A1A17"
ELEVATED_BG <- if (THEME == "light") "#FFFDF6" else "#242420"
INK         <- if (THEME == "light") "#1A1A17" else "#F0EFE8"
INK_SOFT    <- if (THEME == "light") "#4A4A44" else "#B8B7B0"
INK_MUTED   <- if (THEME == "light") "#6B6A63" else "#A8A79F"

IMPRINT_PALETTE <- c(
  "#009E73", "#C475FD", "#4467A3", "#BD8233",
  "#AE3030", "#2ABCCD", "#954477", "#99B314"
)

PHASE_COLORS <- c(
  "Requirements" = IMPRINT_PALETTE[1],
  "Design"       = IMPRINT_PALETTE[2],
  "Development"  = IMPRINT_PALETTE[3],
  "Testing"      = IMPRINT_PALETTE[4]
)

# --- Data ---
# Software development project: tasks with y positions (top = high y)
tasks <- data.frame(
  task_id = 1:9,
  label   = c("Stakeholder Interviews", "Requirements Doc",
              "UI Wireframes", "System Architecture",
              "Frontend Development", "Backend Development",
              "API Integration", "Unit Testing", "UAT"),
  phase   = c("Requirements", "Requirements",
              "Design", "Design",
              "Development", "Development", "Development",
              "Testing", "Testing"),
  start   = as.Date(c("2024-01-08", "2024-01-29",
                      "2024-02-19", "2024-02-19",
                      "2024-03-18", "2024-03-11",
                      "2024-04-15",
                      "2024-05-13", "2024-06-10")),
  end     = as.Date(c("2024-01-19", "2024-02-09",
                      "2024-03-08", "2024-03-01",
                      "2024-04-05", "2024-03-29",
                      "2024-05-03",
                      "2024-05-31", "2024-06-28")),
  y_pos   = c(12, 11, 9, 8, 6, 5, 4, 2, 1),
  stringsAsFactors = FALSE
)

# Phase aggregate bars (span earliest start to latest end per phase)
phases <- data.frame(
  label = c("Requirements", "Design", "Development", "Testing"),
  phase = c("Requirements", "Design", "Development", "Testing"),
  start = as.Date(c("2024-01-08", "2024-02-19", "2024-03-11", "2024-05-13")),
  end   = as.Date(c("2024-02-09", "2024-03-08", "2024-05-03", "2024-06-28")),
  y_pos = c(13, 10, 7, 3),
  stringsAsFactors = FALSE
)

# Y-axis breaks: all 13 rows (phases uppercase, tasks indented)
all_y_pos <- c(phases$y_pos, tasks$y_pos)
all_y_lab <- c(toupper(phases$label), paste0("  ", tasks$label))
y_ord     <- order(all_y_pos)
all_y_pos <- all_y_pos[y_ord]
all_y_lab <- all_y_lab[y_ord]

# Dependencies: finish-to-start (from task_id -> to task_id)
dep_from <- c(2, 2, 3, 4, 5, 6, 7, 8)
dep_to   <- c(3, 4, 5, 6, 7, 7, 8, 9)
# Critical path: Requirements Doc -> UI Wireframes -> Frontend -> API -> Unit Testing -> UAT
is_critical <- c(TRUE, FALSE, TRUE, FALSE, TRUE, FALSE, TRUE, TRUE)

# Build 3-segment L-shaped dependency arrows
offset_days <- 3
seg1_list <- vector("list", length(dep_from))
seg2_list <- vector("list", length(dep_from))
seg3_list <- vector("list", length(dep_from))

for (i in seq_along(dep_from)) {
  from_row <- tasks[tasks$task_id == dep_from[i], ]
  to_row   <- tasks[tasks$task_id == dep_to[i], ]
  x1 <- from_row$end
  y1 <- from_row$y_pos
  x2 <- to_row$start
  y2 <- to_row$y_pos
  mx <- x1 + offset_days
  crit <- is_critical[i]
  seg1_list[[i]] <- data.frame(x = x1, xend = mx, y = y1, yend = y1, critical = crit)
  seg2_list[[i]] <- data.frame(x = mx, xend = mx, y = y1, yend = y2, critical = crit)
  seg3_list[[i]] <- data.frame(x = mx, xend = x2, y = y2, yend = y2, critical = crit)
}

seg1_df <- do.call(rbind, seg1_list)
seg2_df <- do.call(rbind, seg2_list)
seg3_df <- do.call(rbind, seg3_list)

seg1_crit  <- seg1_df[seg1_df$critical, ]
seg1_ncrit <- seg1_df[!seg1_df$critical, ]
seg2_crit  <- seg2_df[seg2_df$critical, ]
seg2_ncrit <- seg2_df[!seg2_df$critical, ]
seg3_crit  <- seg3_df[seg3_df$critical, ]
seg3_ncrit <- seg3_df[!seg3_df$critical, ]

# --- Plot ---
p <- ggplot() +

  # Phase summary bars (wide, transparent — show aggregate phase duration)
  geom_rect(
    data = phases,
    aes(xmin = start, xmax = end,
        ymin = y_pos - 0.26, ymax = y_pos + 0.26,
        fill = phase),
    alpha = 0.3
  ) +

  # Individual task bars
  geom_rect(
    data = tasks,
    aes(xmin = start, xmax = end,
        ymin = y_pos - 0.38, ymax = y_pos + 0.38,
        fill = phase),
    alpha = 0.88
  ) +

  # Non-critical dependency connectors (subdued)
  geom_segment(
    data = seg1_ncrit,
    aes(x = x, xend = xend, y = y, yend = yend),
    color = INK_SOFT, linewidth = 0.45
  ) +
  geom_segment(
    data = seg2_ncrit,
    aes(x = x, xend = xend, y = y, yend = yend),
    color = INK_SOFT, linewidth = 0.45
  ) +
  geom_segment(
    data = seg3_ncrit,
    aes(x = x, xend = xend, y = y, yend = yend),
    color = INK_SOFT, linewidth = 0.45,
    arrow = arrow(length = unit(0.12, "cm"), type = "closed")
  ) +

  # Critical-path dependency connectors (emphasized)
  geom_segment(
    data = seg1_crit,
    aes(x = x, xend = xend, y = y, yend = yend),
    color = INK, linewidth = 0.65
  ) +
  geom_segment(
    data = seg2_crit,
    aes(x = x, xend = xend, y = y, yend = yend),
    color = INK, linewidth = 0.65
  ) +
  geom_segment(
    data = seg3_crit,
    aes(x = x, xend = xend, y = y, yend = yend),
    color = INK, linewidth = 0.65,
    arrow = arrow(length = unit(0.14, "cm"), type = "closed")
  ) +

  scale_fill_manual(values = PHASE_COLORS, name = "Phase") +
  scale_x_date(
    date_breaks = "1 month",
    labels      = scales::label_date_short(),
    expand      = expansion(mult = c(0.01, 0.02))
  ) +
  scale_y_continuous(
    breaks = all_y_pos,
    labels = all_y_lab,
    expand = expansion(add = c(0.6, 0.6))
  ) +
  labs(
    title    = "gantt-dependencies · r · ggplot2 · anyplot.ai",
    subtitle = "Arrows: finish-to-start dependencies; bold = critical path",
    x        = NULL,
    y        = NULL
  ) +
  theme_minimal(base_size = 8) +
  theme(
    plot.background    = element_rect(fill = PAGE_BG, color = PAGE_BG),
    panel.background   = element_rect(fill = PAGE_BG, color = NA),
    panel.grid.major.x = element_line(color = INK_SOFT, linewidth = 0.18,
                                      linetype = "dotted"),
    panel.grid.major.y = element_blank(),
    panel.grid.minor   = element_blank(),
    axis.text.x        = element_text(color = INK_SOFT, size = 8),
    axis.text.y        = element_text(color = INK_SOFT, size = 8, hjust = 1),
    axis.ticks.x       = element_line(color = INK_SOFT),
    axis.ticks.y       = element_blank(),
    axis.line.x        = element_line(color = INK_SOFT),
    plot.title         = element_text(color = INK, size = 12, face = "bold",
                                      margin = margin(b = 3)),
    plot.subtitle      = element_text(color = INK_MUTED, size = 8,
                                      margin = margin(b = 10)),
    legend.background  = element_rect(fill = ELEVATED_BG, color = NA),
    legend.text        = element_text(color = INK_SOFT, size = 8),
    legend.title       = element_text(color = INK, size = 9),
    legend.position    = "bottom",
    legend.direction   = "horizontal",
    legend.key.size    = unit(0.4, "cm"),
    plot.margin        = margin(t = 10, r = 15, b = 5, l = 5)
  )

# --- Save ---
ggsave(
  filename = sprintf("plot-%s.png", THEME),
  plot     = p,
  device   = ragg::agg_png,
  width    = 8,
  height   = 4.5,
  units    = "in",
  dpi      = 400
)

Retrieve this implementation

Runnable source as JSON, for any HTTP client: https://api.anyplot.ai/specs/gantt-dependencies/ggplot2/code. Any spec id and library id listed in llms-full.txt fit the same URL shape; every URL below is complete and callable.

{
  "spec_id": "gantt-dependencies",
  "language": "r",
  "library": "ggplot2",
  "page": "https://anyplot.ai/gantt-dependencies/r/ggplot2",
  "hub": "https://anyplot.ai/gantt-dependencies",
  "code_json": "https://api.anyplot.ai/specs/gantt-dependencies/ggplot2/code",
  "spec_json": "https://api.anyplot.ai/specs/gantt-dependencies",
  "render_light_png": "https://storage.googleapis.com/anyplot-images/plots/gantt-dependencies/r/ggplot2/plot-light.png",
  "render_dark_png": "https://storage.googleapis.com/anyplot-images/plots/gantt-dependencies/r/ggplot2/plot-dark.png",
  "quality_score": 88.0,
  "license": "MIT",
  "guide": "https://anyplot.ai/llms.txt"
}

Part of Gantt Chart with Dependencies on anyplot.ai.

Other implementations