ggtargets creates metro-style diagrams of the dependency graphs of targets pipelines, and of other directed acyclic graphs, using ggplot2.

There are several ways to install ggtargets: from GitHub, from r-universe, with pixi, or with conda. Choose your preferred method.
Install from GitHub:
# install.packages("remotes")
remotes::install_github("royfrancis/ggtargets")
Install from r-universe:
install.packages(
"ggtargets",
repos = c("https://royfrancis.r-universe.dev", "https://cloud.r-project.org")
)
Install with pixi from the prefix.dev channel:
pixi workspace channel add --prepend https://prefix.dev/royfrancis/ggtargets
pixi add r-ggtargets
pixi add r-targets # only needed for tar_ggplot()
Install with conda from the prefix.dev channel:
conda install -c https://prefix.dev/royfrancis/ggtargets -c conda-forge r-ggtargets
conda install -c conda-forge r-targets # only needed for tar_ggplot()
Inside a targets project:
library(ggtargets)
tar_ggplot()
tar_ggplot(node_color = "status", palette = targets_status_colors())
targets pipelinewithr::with_dir(withr::local_tempdir(), {
targets::tar_script(list(
tar_target(n, 20),
tar_target(raw_data, data.frame(x = seq_len(n), y = seq_len(n) * 2 + rnorm(n))),
tar_target(clean, raw_data[complete.cases(raw_data), ]),
tar_target(model, lm(y ~ x, data = clean)),
tar_target(summary, coef(model)),
tar_target(report, capture.output(print(summary)))
))
tar_ggplot()
})

Color by build status to see what's out of date at a glance:
withr::with_dir(withr::local_tempdir(), {
targets::tar_script(list(
tar_target(n, 20),
tar_target(raw_data, data.frame(x = seq_len(n), y = seq_len(n) * 2 + rnorm(n))),
tar_target(clean, raw_data[complete.cases(raw_data), ]),
tar_target(model, lm(y ~ x, data = clean)),
tar_target(summary, coef(model)),
tar_target(report, capture.output(print(summary)))
))
targets::tar_make(reporter = "silent")
tar_ggplot(node_color = "status", palette = targets_status_colors())
})

Nodes are drawn by their type, as targets::tar_visnetwork() does: a circle for a regular target, a square for a dynamic-branching one, and an up and a down triangle for imported functions and objects (which are in the graph with targets_only = FALSE). A legend names the shapes:
withr::with_dir(withr::local_tempdir(), {
targets::tar_script({
simulate <- function(group) rnorm(10, mean = match(group, letters))
list(
tar_target(groups, letters[1:3]),
tar_target(data, simulate(groups), pattern = map(groups)),
tar_target(means, vapply(data, mean, numeric(1)))
)
})
tar_ggplot(targets_only = FALSE)
})

A pipeline of many targets is easier to read as a few stages. targets has no tag field for a target, but its description is free text, so a stage can go there (tar_option_set(description = ) sets it for a block of targets). collapse_by = "description" merges the targets that share one into a node, with their number in the label, and node_size and edge_width show those counts:
withr::with_dir(withr::local_tempdir(), {
targets::tar_script({
tar_option_set(description = "ingest")
ingest <- list(
tar_target(raw_data, 1:10),
tar_target(clean_data, raw_data * 2),
tar_target(features, clean_data + 1)
)
tar_option_set(description = "model")
model <- list(
tar_target(fit_mean, mean(features)),
tar_target(fit_median, median(features))
)
tar_option_set(description = "report")
report <- list(tar_target(report_text, c(fit_mean, fit_median)))
tar_option_set(description = character(0))
c(ingest, model, report)
})
tar_ggplot(collapse_by = "description", node_size = "n", edge_width = "n")
})

See the "Collapsing a pipeline by stage" section of ?tar_ggplot for the details, and for a column of your own instead of description.
ggplot_dag()/as_dag() work with any edge-list data frame, not just a targets pipeline:
edges <- data.frame(from = c("a", "a", "b", "c"), to = c("b", "c", "d", "d"))
nodes <- data.frame(name = c("a", "b", "c", "d"), type = c("input", "process", "process", "output"))
ggplot_dag(as_dag(edges, nodes = nodes), node_color = "type")

See vignette("ggtargets") to get started, and browseVignettes("ggtargets") for more (customizing the style, other inputs, how the layout works). A few more articles
(collapsing a pipeline, colors, working with ggplot2, a gallery) are pkgdown-only for now, since the package doesn't ship them to keep install-time dependencies light - build the site locally with pkgdown::build_site() to read them.
The metro style and its default color palette are reproduced from dagviz by Wim Yedema (Apache-2.0), and the workflow-diagram use case is inspired by snakevision by OpenOmics (MIT). The code was mostly rewritten by Anthropic AI models Claude Sonnet 5 and Opus 5.5. ggtargets is an independent package: it is not part of rOpenSci's targets project.
2026 • Roy Francis