diff --git a/NEWS.md b/NEWS.md
index b3e8922c..cce8eba0 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -1,5 +1,7 @@
# bayesplot (development version)
+* Added vignette sections demonstrating `*_data()` companion functions for
+ building custom ggplot2 visualizations (#435)
* Documentation added for all exported `*_data()` functions (#209)
* Improved documentation for `binwidth`, `bins`, and `breaks` arguments to clarify they are passed to `ggplot2::geom_area()` and `ggdist::stat_dots()` in addition to `ggplot2::geom_histogram()`
* Improved documentation for `freq` argument to clarify it applies to frequency polygons in addition to histograms
diff --git a/vignettes/graphical-ppcs.Rmd b/vignettes/graphical-ppcs.Rmd
index db649f42..d65a74d6 100644
--- a/vignettes/graphical-ppcs.Rmd
+++ b/vignettes/graphical-ppcs.Rmd
@@ -314,6 +314,45 @@ See Figure 8 in [Gabry et al. (2019)](#gabry2019) for another example of using
+## Using `*_data()` functions for custom plots
+
+Many bayesplot plotting functions have a companion `*_data()` function that
+returns the pre-processed data as a tidy data frame instead of a plot. This is
+useful when you want to build a fully custom ggplot2 visualization using the
+same summary statistics that bayesplot computes internally.
+
+For example, `ppc_intervals_data()` returns the quantile summaries that
+`ppc_intervals()` uses:
+
+```{r data_intervals, eval=params$EVAL}
+d <- ppc_intervals_data(y, yrep, prob = 0.5, prob_outer = 0.9)
+head(d)
+```
+
+You can then use this data to create your own plot:
+
+```{r data_intervals_custom, eval=params$EVAL}
+ggplot(d, aes(x = x, y = m)) +
+ geom_linerange(aes(ymin = ll, ymax = hh), color = "skyblue", linewidth = 0.6) +
+ geom_linerange(aes(ymin = l, ymax = h), color = "steelblue", linewidth = 1.2) +
+ geom_point(aes(y = y_obs), shape = 21, fill = "red", size = 1.5) +
+ labs(title = "Custom interval plot from ppc_intervals_data()",
+ x = "Observation", y = "Value") +
+ theme_minimal()
+```
+
+Similarly, `ppc_stat_data()` returns the computed test statistics:
+
+```{r data_stat, eval=params$EVAL, message=FALSE}
+stat_d <- ppc_stat_data(y, yrep, stat = "median")
+head(stat_d)
+```
+
+See `available_ppc(plots_only = FALSE)` and `available_mcmc(plots_only = FALSE)`
+for a full list of data-preparation functions.
+
+
+
## Providing an interface to bayesplot PPCs from another package
The **bayesplot** package provides the S3 generic function `pp_check`. Authors of
diff --git a/vignettes/plotting-mcmc-draws.Rmd b/vignettes/plotting-mcmc-draws.Rmd
index 0ed21f62..4dbd8b38 100644
--- a/vignettes/plotting-mcmc-draws.Rmd
+++ b/vignettes/plotting-mcmc-draws.Rmd
@@ -367,6 +367,23 @@ mcmc_trace_highlight(posterior, pars = "sigma", highlight = 3)
```
+
+
+## Using `*_data()` functions for custom plots
+
+As with PPC functions, many MCMC plotting functions have `*_data()` companions
+that return the underlying data instead of a plot. For example,
+`mcmc_intervals_data()` returns the quantiles used by `mcmc_intervals()`:
+
+```{r data_intervals_mcmc}
+d <- mcmc_intervals_data(posterior, pars = c("(Intercept)", "sigma"))
+d
+```
+
+This can be used to build fully custom ggplot2 visualizations using the same
+summary statistics that bayesplot computes internally. See
+`available_mcmc(plots_only = FALSE)` for a full list of `*_data()` functions.
+
## References