Overview

By 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.


Step 1: Install and Load Packages

# 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)

Step 2: Download and Install a Font

  1. Go to fonts.google.com and find a font you like
  2. Click “Get font”“Download all” — this downloads a .zip file
  3. Unzip it and find the .ttf or .otf file(s) inside
  4. Install it:
    • Windows: Right-click the .ttf file → “Install for all users” (or just “Install” to install for your user only)
    • Mac: Double-click the .ttf file → click “Install Font”

Once installed, the font is available system-wide — in Word, PowerPoint, and now R.


Step 3: Load the Font into 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 family argument in font_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 in theme() later.


Step 4: Use the Font in a ggplot

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)
  )


Step 5: Load Multiple Fonts

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)
  )


Step 6: Build a Reusable Theme Function

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)


Saving Plots with Custom Fonts

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 before ggsave(), not after.


Common Issues

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

Registering Bold and Italic Variants

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
)

Quick Reference

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)