showtextBy default, R’s graphics use a small set of built-in fonts. With the
showtext and systemfonts packages, you can use
any font from Google Fonts in your ggplot2 charts — on
both Mac and PC.
The approach here is to install fonts to your system first, then load them into R from the local file. This is faster and more reliable than having R download fonts from the internet every time you run your script.
# Install these once (remove the # to run)
# install.packages("showtext")
# install.packages("sysfonts")
# install.packages("systemfonts")
# install.packages("ggplot2")
library(showtext) # renders custom fonts in R graphics
library(sysfonts) # font_add() for loading local fonts
library(systemfonts) # match_font() for finding installed fonts
library(ggplot2)
.zip file.ttf or .otf file(s)
inside.ttf file →
“Install for all users” (or just “Install” to
install for your user only).ttf file →
click “Install Font”Once installed, the font is available system-wide — in Word, PowerPoint, and now R.
After installing, use systemfonts::match_font() to
locate the font file automatically, then register it with
font_add(). This works on both Mac and PC without any
hardcoded file paths.
# Find the installed font file automatically (works on Mac and PC)
font_path <- match_font("Lato")$path
# Register it with showtext — give it any nickname you want
font_add(family = "my_font", regular = font_path)
# Tell showtext to automatically render fonts in all plots
showtext_auto()
# Set DPI — use 300 for saving
showtext_opts(dpi = 300)
Note: The
familyargument infont_add()is just a nickname R uses internally. You can call it anything —"my_font","lato","custom", etc. Just be consistent: whatever you name it here is what you’ll reference intheme()later.
Once registered, pass the nickname to family inside
theme().
ggplot(mpg, aes(x = displ, y = hwy)) +
geom_point(color = "steelblue", alpha = 0.7) +
labs(
title = "Engine Displacement vs. Highway MPG",
subtitle = "Using a custom installed font",
x = "Engine Displacement (L)",
y = "Highway MPG"
) +
theme_minimal() +
theme(
text = element_text(family = "my_font", size = 14),
plot.title = element_text(family = "my_font", size = 18, face = "bold"),
plot.subtitle = element_text(family = "my_font", size = 13)
)
You can load as many fonts as you want — install each one to your system first, then register each with a different nickname.
# Install both fonts to your system first, then:
font_add(family = "title_font", regular = match_font("Playfair Display")$path)
font_add(family = "body_font", regular = match_font("Source Sans 3")$path)
showtext_auto()
ggplot(mpg, aes(x = class)) +
geom_bar(fill = "steelblue") +
labs(
title = "Vehicle Count by Class",
subtitle = "Title uses Playfair Display; body uses Source Sans 3",
x = "Vehicle Class",
y = "Count"
) +
theme_minimal() +
theme(
plot.title = element_text(family = "title_font", size = 20),
plot.subtitle = element_text(family = "body_font", size = 13),
axis.text = element_text(family = "body_font", size = 11),
axis.title = element_text(family = "body_font", size = 12)
)
Once you’ve settled on a font, wrap everything into a custom theme function so you don’t repeat yourself across scripts. Load fonts and set options once at the top of your script, then just call your theme function on every plot.
# --- Run once at the top of any script that uses your theme ---
font_add(family = "my_font", regular = match_font("Lato")$path)
showtext_auto()
showtext_opts(dpi = 300) # good size for saving
# --- Define your theme ---
theme_custom <- function(base_size = 14, legend = FALSE) {
base <- theme_minimal() +
theme(
text = element_text(size = base_size, family = "my_font"),
axis.text = element_text(family = "my_font"),
axis.title = element_text(family = "my_font"),
strip.text = element_text(size = base_size + 2, family = "my_font")
)
if (legend) {
base + theme(
legend.position = "bottom",
legend.title = element_blank(),
legend.text = element_text(family = "my_font")
)
} else {
base + theme(legend.position = "none")
}
}
ggplot(mpg, aes(x = displ, y = hwy, color = drv)) +
geom_point(alpha = 0.7) +
labs(title = "This uses theme_custom()", x = "Displacement", y = "Hwy MPG") +
theme_custom(legend = TRUE)
p <- ggplot(mpg, aes(x = displ, y = hwy)) +
geom_point(color = "steelblue") +
labs(title = "Saved with Custom Font") +
theme_custom()
ggsave("my_plot.png", plot = p, width = 9, height = 6, dpi = 300)
Tip: If fonts look blurry or don’t appear in your saved file, make sure
showtext_opts(dpi = 300)is called beforeggsave(), not after.
| Problem | Solution |
|---|---|
match_font() returns a system default, not your
font |
The font isn’t installed yet — go back to Step 2 |
| Font not showing in plot | Make sure showtext_auto() is called after
font_add() |
| Font looks blurry when saved | Set showtext_opts(dpi = 300) before
ggsave() |
| Font works in script but not in R Markdown | Call showtext_auto() inside a code chunk, not just the
console |
| Bold/italic not rendering correctly | You may need to register those variants separately — see below |
Some fonts have separate files for bold and italic. If you want
face = "bold" to work correctly, register those files
too:
font_add(
family = "my_font",
regular = match_font("Lato", bold = FALSE, italic = FALSE)$path,
bold = match_font("Lato", bold = TRUE, italic = FALSE)$path,
italic = match_font("Lato", bold = FALSE, italic = TRUE)$path
)
library(showtext)
library(sysfonts)
library(systemfonts)
# 1. Install font to your system (double-click the .ttf file)
# 2. Load font into R
font_add(family = "my_font", regular = match_font("Lato")$path)
showtext_auto()
showtext_opts(dpi = 96) # 300 for saving
# 3. Use in theme
theme(text = element_text(family = "my_font"))
# 4. Before saving
showtext_opts(dpi = 300)
ggsave("plot.png", width = 9, height = 6, dpi = 300)