ggplot2 ggsave() in R: Save Plots to PNG, PDF, and SVG
The ggsave() function in ggplot2 writes the last (or a named) ggplot to disk. It picks the file format from the extension and lets you set width, height, units, and DPI for print-ready output.
ggsave("plot.png") # last plot, default size
ggsave("plot.png", plot = p) # a named plot object
ggsave("plot.png", width = 8, height = 5) # inches by default
ggsave("plot.png", width = 20, height = 12, units = "cm") # metric units
ggsave("plot.png", dpi = 300) # print resolution
ggsave("plot.pdf") # vector PDF
ggsave("plot.svg") # vector SVG
ggsave("plot.png", bg = "white") # force white backgroundNeed explanation? Read on for examples and pitfalls.
What ggsave() does in one sentence
ggsave() writes a ggplot to a file using the extension to choose the graphics device. Pass it a filename; it grabs the last displayed plot, infers PNG, PDF, SVG, JPEG, TIFF, BMP, EPS, or WMF from the suffix, and writes to your working directory.
Because the format follows the filename, you almost never need to call png() or pdf() directly when you are already in ggplot2. The function also accepts a plot object, custom dimensions, and DPI for control over what gets saved.
Syntax
ggsave() takes a filename and optional plot, size, and device arguments. The minimum call is one string: a path with a known extension. Everything else has a sensible default.
The full signature:
ggsave(filename, plot = last_plot(), device = NULL, path = NULL,
scale = 1, width = NA, height = NA, units = c("in", "cm", "mm", "px"),
dpi = 300, limitsize = TRUE, bg = NULL, ...)
Most arguments default to the current graphics device size, so a bare ggsave("plot.png") is often enough during interactive work. For reports and publications, pin width, height, and dpi explicitly.
Seven common patterns
1. Save the last plot
No plot = argument needed. ggsave() defaults to last_plot(), which is whichever ggplot was rendered last in the session.
2. Save a specific plot object
Pass plot = p to bypass last_plot(). Useful when you generated several plots and want to save one that is not the most recent.
3. Set width, height, and units
Default unit is inches. Switch to "cm" or "mm" for print work; switch to "px" for exact-pixel exports for web use.
4. Set DPI for print or retina screens
DPI is dots per inch. Web uses 72 to 96 DPI; print typically 300; sharp screenshots for retina displays 320. Higher DPI multiplies pixel dimensions, not visual size.
5. Export to vector PDF or SVG
PDF and SVG keep text and shapes as vectors, so the file zooms without pixelation. Best for academic figures and slide decks. SVG also opens in any browser and edits in Inkscape or Illustrator.
6. Force a white background
ggplot2 themes like theme_minimal() have transparent panel backgrounds. PNG inherits that, which looks broken on dark slides. Pass bg = "white" (or any color) to force an opaque background.
7. Save many plots in a loop
ggsave() returns invisibly, so it composes well with for loops, purrr::walk(), or lapply(). Each call writes one file; nothing accumulates in memory.
ggsave() vs the base R device pipeline
ggsave() collapses the open-device, plot, close-device dance into one call. Base R uses png("file.png"); plot(...); dev.off(). ggsave handles all three steps and picks the device from the extension.
| Aspect | ggsave() |
png() / pdf() + dev.off() |
|---|---|---|
| Lines of code | 1 | 3 |
| Format selection | From extension | Explicit function |
| Default size source | Current graphics device | Function argument required |
| Units | in, cm, mm, px | px (png), in (pdf) |
| Works with non-ggplot output | No | Yes |
| Forgets to close device? | Cannot | Common bug |
Use ggsave() for ggplots. Use the base device pipeline for lattice plots, base graphics, or when wrapping several plots in one PDF (pdf() with multiple page-creating calls).
p1 + p2 (patchwork) or plot_grid(p1, p2) (cowplot), then pass the result to ggsave(). The combined object is still a ggplot under the hood.Common pitfalls
Three traps catch most users.
- Forgetting
bg = "white": Themes with transparent panels save with no background fill. Slides and Word documents render the transparent panel as black or white inconsistently. Always setbgfor shared output. - Setting
dpiwithoutwidthandheight: DPI scales pixel count, but the inch dimensions still come from the current device. Result: a tiny 300-DPI file because the device was 3 inches wide. Set width and height first. - Mismatched extension and
device =:ggsave("plot.png", device = "pdf")writes PDF bytes into a.pngfile. Image viewers refuse to open it. Either trust the extension (omitdevice) or match them manually.
limitsize = TRUE blocks dimensions over 50 inches per side. Run ggsave("plot.png", width = 60, height = 30) and it errors. Set limitsize = FALSE to override, but check your units first; you usually meant cm.Try it yourself
Try it: Save the mtcars scatter (p from earlier) as mtcars-try.pdf at 7 by 5 inches with DPI 300. Confirm the file exists.
Click to reveal solution
Explanation: Extension .pdf triggers the PDF device. Width, height, and units fix the physical page; dpi affects embedded rasters only (vector content stays vector).
Related ggplot2 output functions
last_plot(): returns the most recently displayed ggplot, what ggsave defaults toggplot2::ggplot_build(): inspect the computed plot data before savinggrDevices::png(),pdf(),svg(): the lower-level devices ggsave wrapsgridExtra::grid.arrange(): combine plots for one ggsave callpatchwork::wrap_plots(): modern composition; saves cleanly with ggsave
For deep control of the underlying device (custom fonts, anti-aliasing, Cairo backend), pass device = ragg::agg_png or device = cairo_pdf to ggsave.
FAQ
Why does ggsave save the wrong plot?
ggsave() uses last_plot() when no plot = argument is given. If you ran another ggplot between the one you wanted and the ggsave() call, the wrong plot is saved. Either assign the target plot to a variable and pass it explicitly with plot = p, or call ggsave() immediately after rendering the intended plot.
What units does ggsave use by default?
The default is inches. Pass units = "cm", units = "mm", or units = "px" to switch. Pixels require ggplot2 3.3.0 or newer. For print, cm and mm match journal column specifications; for web, pixels match the final rendered size.
Can ggsave export plots in multiple formats at once?
Not in a single call, but a short loop does it cleanly. Use for (ext in c("png","pdf","svg")) ggsave(paste0("plot.", ext), plot = p). Each format writes its own file, and the same dimensions translate between raster and vector.
Why is my saved plot blurry or pixelated?
PNG and JPEG are raster formats: they store a fixed pixel grid. A 6-inch plot at 72 DPI is 432 pixels wide; resizing it in slides multiplies the pixels and blurs the image. Raise dpi to 300 for print or use PDF / SVG for documents that zoom.
How do I save a plot without showing it?
Wrap the plot in ggsave() directly: ggsave("plot.png", plot = ggplot(mtcars, aes(wt, mpg)) + geom_point()). The plot is built but never sent to the active graphics device. Useful for batch scripts and Shiny apps that should not flash a render in the UI.
For more on building plots before saving, see ggplot2 Tutorial With R.