Using custom fonts and plotting with error bars

Author

Michael Stadler, Charlotte Soneson

Summary

This document illustrates how to use systemfonts and ragg together with ggplot2 to:

  • make a plot that uses custom fonts
  • make a plot with error bars

Prepare data

Run the following code to prepare the data used in this document:

suppressPackageStartupMessages({
    library(dplyr)
    library(tibble)
})

# built-in `mtcars` dataset (see ?mtcars)
# ... convert some columns to factors
mycars <- mtcars
mycars$cyl <- factor(mycars$cyl, levels = c("4","6","8"))
mycars$engine_shape <- factor(c("0" = "V-shaped", "1" = "straight")[as.character(mycars$vs)])
mycars$transmission <- factor(c("0" = "automatic", "1" = "manual")[as.character(mycars$am)])

# ... summarize power by cylinders
mycars_summary <- mycars |>
    group_by(cyl) |>
    summarise(hp_avg = mean(hp),
              hp_sem = sd(hp) / sqrt(length(hp)),
              .groups = "drop")

tibble(mycars)
# A tibble: 32 × 13
     mpg cyl    disp    hp  drat    wt  qsec    vs    am  gear  carb
   <dbl> <fct> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
 1  21   6      160    110  3.9   2.62  16.5     0     1     4     4
 2  21   6      160    110  3.9   2.88  17.0     0     1     4     4
 3  22.8 4      108     93  3.85  2.32  18.6     1     1     4     1
 4  21.4 6      258    110  3.08  3.22  19.4     1     0     3     1
 5  18.7 8      360    175  3.15  3.44  17.0     0     0     3     2
 6  18.1 6      225    105  2.76  3.46  20.2     1     0     3     1
 7  14.3 8      360    245  3.21  3.57  15.8     0     0     3     4
 8  24.4 4      147.    62  3.69  3.19  20       1     0     4     2
 9  22.8 4      141.    95  3.92  3.15  22.9     1     0     4     2
10  19.2 6      168.   123  3.92  3.44  18.3     1     0     4     4
# ℹ 22 more rows
# ℹ 2 more variables: engine_shape <fct>, transmission <fct>
tibble(mycars_summary)
# A tibble: 3 × 3
  cyl   hp_avg hp_sem
  <fct>  <dbl>  <dbl>
1 4       82.6   6.31
2 6      122.    9.17
3 8      209.   13.6 

Create figure

Load packages

Code
library(ggplot2)
library(ragg)
library(systemfonts)

# remark: have to set the chunk output device to "ragg_png"
knitr::opts_chunk$set(dev = "ragg_png")

Get fonts

We first need to make the fonts available to R. Either our desired font is already installed on the local system, or we can download one of the many free fonts available on the internet, for example from https://fonts.google.com/.

The systemfonts package helps to list locally installed fonts that R can “see” or make a downloaded font file (typically a TrueType file) available to R.

Code
# list available fonts:
fonts <- systemfonts::system_fonts()
fonts
# A tibble: 777 × 10
   path          index name  family style weight width italic monospace variable
   <chr>         <int> <chr> <chr>  <chr> <ord>  <ord> <lgl>  <lgl>     <lgl>   
 1 /System/Libr…     2 Rock… Rockw… Bold  bold   norm… FALSE  FALSE     FALSE   
 2 /Users/stadl…     0 Apto… Aptos  Ligh… light  norm… TRUE   FALSE     FALSE   
 3 /System/Libr…     0 Note… Notew… Light normal norm… FALSE  FALSE     FALSE   
 4 /System/Libr…     1 Deva… Devan… Bold  bold   norm… FALSE  FALSE     FALSE   
 5 /System/Libr…     0 Kann… Kanna… Regu… normal norm… FALSE  FALSE     FALSE   
 6 /System/Libr…     0 Verd… Verda… Bold  bold   norm… FALSE  FALSE     FALSE   
 7 /Users/stadl…     0 Sour… Sourc… Ital… light  norm… TRUE   TRUE      TRUE    
 8 /System/Libr…     8 Aria… Arial… Light light  norm… FALSE  FALSE     FALSE   
 9 /System/Libr…    10 Appl… Apple… Thin  thin   norm… FALSE  FALSE     FALSE   
10 /System/Libr…     0 Deco… DecoT… Regu… normal norm… FALSE  FALSE     FALSE   
# ℹ 767 more rows
Code
unique(fonts$family)
  [1] "Rockwell"                   "Aptos"                     
  [3] "Noteworthy"                 "Devanagari MT"             
  [5] "Kannada Sangam MN"          "Verdana"                   
  [7] "Source Code Pro ExtraLight" "Arial Hebrew Scholar"      
  [9] "Apple SD Gothic Neo"        "DecoType Naskh"            
 [11] "Trebuchet MS"               "Khmer MN"                  
 [13] "PingFang SC"                "Mishafi Gold"              
 [15] "Aller"                      "DejaVu Serif"              
 [17] "Hasklig"                    "Inconsolata"               
 [19] "Marker Felt"                "Damascus"                  
 [21] "Chalkduster"                "Roboto Mono"               
 [23] "STIX Two Text"              "Songti TC"                 
 [25] "Avenir"                     "DejaVu Sans"               
 [27] "Corsiva Hebrew"             "Gurmukhi Sangam MN"        
 [29] "Inconsolata Expanded"       "Times New Roman"           
 [31] "Al Bayan"                   "Noto Sans Tagalog"         
 [33] "Kohinoor Gujarati"          "Raleway"                   
 [35] "Avenir Next Condensed"      "Inconsolata SemiCondensed" 
 [37] "Gurmukhi MN"                "PingFang HK"               
 [39] "Gurmukhi MT"                "PingFang MO"               
 [41] "Impact"                     "Noto Sans Kannada"         
 [43] "Hiragino Sans"              "Baskerville"               
 [45] "Arial Narrow"               "Noto Sans Syriac"          
 [47] "Plantagenet Cherokee"       "Songti SC"                 
 [49] "Lucida Grande"              "Palatino"                  
 [51] "Grantha Sangam MN"          "Bangla Sangam MN"          
 [53] "Apple Symbols"              "Inconsolata ExtraCondensed"
 [55] "Geneva"                     "Heiti SC"                  
 [57] "Bodoni 72"                  "PT Serif Caption"          
 [59] "Avenir Next"                "Snell Roundhand"           
 [61] "ITF Devanagari Marathi"     "Malayalam Sangam MN"       
 [63] "Myanmar Sangam MN"          "PT Sans"                   
 [65] "Gill Sans"                  "Mishafi"                   
 [67] "Skia"                       "GB18030 Bitmap"            
 [69] "Wingdings 2"                "Gujarati MT"               
 [71] "New Peninim MT"             "Helvetica Neue"            
 [73] "Mshtakan"                   "Gujarati Sangam MN"        
 [75] "PingFang TC"                "Phosphate"                 
 [77] "Wingdings"                  "Inconsolata SemiExpanded"  
 [79] "STSong"                     "Georgia"                   
 [81] "Sukhumvit Set"              "PT Mono"                   
 [83] "American Typewriter"        "Charter"                   
 [85] "Noto Sans Myanmar"          "Source Sans 3"             
 [87] "PT Serif"                   "Devanagari Sangam MN"      
 [89] "PT Sans Narrow"             "DejaVu Sans Mono"          
 [91] "Inconsolata UltraCondensed" "Mukta Mahee"               
 [93] "Inconsolata Condensed"      "Didot"                     
 [95] "Webdings"                   "STIX Two Math"             
 [97] "DejaVu Math TeX Gyre"       "Shree Devanagari 714"      
 [99] "Hoefler Text"               "Oriya Sangam MN"           
[101] "Monoisome"                  "Andale Mono"               
[103] "Symbol"                     "Bangla MN"                 
[105] "Kohinoor Telugu"            "ITF Devanagari"            
[107] "Telugu Sangam MN"           "Inconsolata UltraExpanded" 
[109] "Kohinoor Devanagari"        "PT Sans Caption"           
[111] "Apple Braille"              "Sinhala MN"                
[113] "Raanana"                    "Malayalam MN"              
[115] "Oriya MN"                   "Thonburi"                  
[117] "Optima"                     "Source Sans Pro"           
[119] "Helvetica"                  "Savoye LET"                
[121] "Myanmar MN"                 "Futura"                    
[123] "Hiragino Sans GB"           "Apple Chancery"            
[125] "Arial Rounded MT Bold"      "Fira Code Light"           
[127] "Courier New"                "Chalkboard SE"             
[129] "Kailasa"                    "Diwan Thuluth"             
[131] "Inconsolata ExtraExpanded"  "Kannada MN"                
[133] "Galvji"                     "Menlo"                     
[135] "Tahoma"                     "TSTAR"                     
[137] "Big Caslon"                 "Fira Mono"                 
[139] "Farisi"                     "Kohinoor Bangla"           
[141] "Arial Black"                "Noto Serif Myanmar"        
[143] "Arial"                      "Tamil Sangam MN"           
[145] "Papyrus"                    "Arial Hebrew"              
[147] "Bodoni 72 Oldstyle"         "Al Nile"                   
[149] "Cochin"                     "Sathu"                     
[151] "Aptos Mono"                 "Bodoni Ornaments"          
[153] "AppleMyungjo"               "Tamil MN"                  
[155] "Noto Sans NKo"              "KufiStandardGK"            
[157] "Kefa"                       "Adobe Pi Std"              
[159] "Fira Mono Medium"           "Heiti TC"                  
[161] "Al Tarikh"                  "AppleGothic"               
[163] "Aptos Narrow"               "Toppan Bunkyu Gothic"      
[165] "DIN Condensed"              "Kokonor"                   
[167] "Microsoft Sans Serif"       "Waseem"                    
[169] "Hiragino Mincho ProN"       "Muna"                      
[171] "Noto Sans Batak"            "Noto Nastaliq Urdu"        
[173] "Brush Script MT"            "Krungthep"                 
[175] "Aptos Serif"                "Lao MN"                    
[177] "Arial Unicode MS"           "Noto Sans Oriya"           
[179] "Apple Color Emoji"          "Euphemia UCAS"             
[181] "Beirut"                     "Luminari"                  
[183] "Party LET"                  "DIN Alternate"             
[185] "Copperplate"                "InaiMathi"                 
[187] "Comic Sans MS"              "Bradley Hand"              
[189] "Academy Engraved LET"       "SignPainter"               
[191] "Nadeem"                     "Monaco"                    
[193] "Ayuthaya"                   "Herculanum"                
[195] "Sana"                       "Hiragino Maru Gothic ProN" 
[197] "Bodoni 72 Smallcaps"        "Khmer Sangam MN"           
[199] "Chalkboard"                 "Diwan Kufi"                
[201] "Sinhala Sangam MN"          "Wingdings 3"               
[203] "Silom"                      "Geeza Pro"                 
[205] "Baghdad"                    "Telugu MN"                 
[207] "Lao Sangam MN"              "Zapfino"                   
[209] "Farah"                      "Zapf Dingbats"             
[211] "Trattatello"               
Code
# download and register an additional font
# ... already registered fonts
systemfonts::registry_fonts()
# A tibble: 0 × 7
# ℹ 7 variables: path <chr>, index <int>, family <chr>, style <chr>,
#   weight <ord>, italic <lgl>, features <list>
Code
# ... download Gochi Hand
download.file(url = "https://github.com/huertatipografica/gochi-hand/archive/master.zip",
              destfile = file.path(tempdir(), "master.zip"))
extracted_files <- unzip(file.path(tempdir(), "master.zip"), exdir = tempdir())
# ...  we typically want a TrueType file (extension `.ttf`)
ttf <- grep(".ttf$", extracted_files, value = TRUE)
ttf
[1] "/var/folders/qm/f4lwc7c10193k2zfy4smk22c0000gr/T//RtmpQt6hGb/gochi-hand-master/fonts/ttf/GochiHand.ttf"
Code
# ... register the newly downloaded font for use in R
systemfonts::register_font(name = "Gochi Hand", plain = ttf)
# ... list the now registered fonts
systemfonts::registry_fonts()
# A tibble: 4 × 7
  path                                 index family style weight italic features
  <chr>                                <int> <chr>  <chr> <ord>  <lgl>  <list>  
1 /var/folders/qm/f4lwc7c10193k2zfy4s…     0 Gochi… Regu… normal FALSE  <int>   
2 /var/folders/qm/f4lwc7c10193k2zfy4s…     0 Gochi… Bold  bold   FALSE  <int>   
3 /var/folders/qm/f4lwc7c10193k2zfy4s…     0 Gochi… Ital… normal TRUE   <int>   
4 /var/folders/qm/f4lwc7c10193k2zfy4s…     0 Gochi… Bold… bold   TRUE   <int>   

Plot

With the fonts available to R, we can now create plots that use custom fonts for some or all of the text elements. Fonts are defined by element_text() using the arguments family (see the family column returned by systemfonts functions) and face (one of “plain”, “italic”, “bold” or “bold.italic”, see style column return by systemfonts functions).

Code
# remember that this will only work if you have
# activated the `ragg` backend to render the plots

# create base plot
p0 <- ggplot(data = mycars, mapping = aes(x = cyl)) +
    coord_cartesian(ylim = c(0, 250)) +
    labs(x = "Number of cylinders", y = "Gross horsepower") +
    theme_bw(20) +
    theme(panel.grid = element_blank())

# ... add bars and Roman font
p0 +
    geom_col(data = mycars_summary, mapping = aes(y = hp_avg),
             fill = "gray70") +
    geom_errorbar(data = mycars_summary,
                  mapping = aes(ymin = hp_avg - hp_sem,
                                ymax = hp_avg + hp_sem),
                  width = 0.6) +
    annotate("text", x = -Inf, y = Inf, hjust = -0.1, vjust = 1.1,
             label = "Question:\nIs this font well readable?",
             family = "Z003", size = 18 / .pt) +
    theme(text = element_text(family = "Z003"))

Code
# ... add points with errorbars and handwriting-style font
p0 +
    geom_pointrange(data = mycars_summary,
                    mapping = aes(y = hp_avg,
                                ymin = hp_avg - hp_sem,
                                ymax = hp_avg + hp_sem),
                    size = 6,
                    color = "firebrick") +
    geom_point(mapping = aes(y = hp), shape = 1) +
    annotate("text", x = -Inf, y = Inf, hjust = -0.1, vjust = 1.1,
             label = "Conclusion:\nMore cylinders give\n increased power",
             family = "Gochi Hand", size = 18 / .pt) +
    annotate("text", x = Inf, y = -Inf, hjust = 1.1, vjust = -0.5,
             label = "mean +/- sem", color = "firebrick",
             family = "Gochi Hand", size = 18 / .pt) +
    theme(text = element_text(family = "Gochi Hand"))

Remarks

The page at https://r-graph-gallery.com/custom-fonts-in-R-and-ggplot2.html describes two ways to use custom fonts with ggplot2:

  • ragg is a graphics backend (it renders the plot to a bitmap graphics device like png()), and can make all system fonts available to be used in R. This is the approach used above. ragg is based on the fast AGG library, does not require you to use any other packages and is also fully integrated into RStudio (you can select is as your default graphics device in “Tools” -> “Global options…” -> “General”, panel “Graphics”).
  • showtext is a package that is especially designed to use custom fonts with pdf() devices. After loading a font and activating showtext, it renders the characters as filled polygons.

Another useful page with more detailed information about using fonts in ggplot2 and R in general: https://yjunechoe.github.io/posts/2021-06-24-setting-up-and-debugging-custom-fonts/

Session info

Code
sessioninfo::session_info()
─ Session info ───────────────────────────────────────────────────────────────
 setting  value
 version  R version 4.5.2 (2025-10-31)
 os       macOS Sequoia 15.7.1
 system   aarch64, darwin20
 ui       X11
 language (EN)
 collate  en_US.UTF-8
 ctype    en_US.UTF-8
 tz       Europe/Zurich
 date     2025-11-03
 pandoc   3.6.4 @ /usr/local/bin/ (via rmarkdown)
 quarto   1.7.28 @ /usr/local/bin/quarto

─ Packages ───────────────────────────────────────────────────────────────────
 package      * version date (UTC) lib source
 cli            3.6.5   2025-04-23 [1] CRAN (R 4.5.0)
 dichromat      2.0-0.1 2022-05-02 [1] CRAN (R 4.5.0)
 digest         0.6.37  2024-08-19 [1] CRAN (R 4.5.0)
 dplyr        * 1.1.4   2023-11-17 [1] CRAN (R 4.5.0)
 evaluate       1.0.5   2025-08-27 [1] CRAN (R 4.5.1)
 farver         2.1.2   2024-05-13 [1] CRAN (R 4.5.0)
 fastmap        1.2.0   2024-05-15 [1] CRAN (R 4.5.0)
 generics       0.1.4   2025-05-09 [1] CRAN (R 4.5.0)
 ggplot2      * 4.0.0   2025-09-11 [1] CRAN (R 4.5.0)
 glue           1.8.0   2024-09-30 [1] CRAN (R 4.5.0)
 gtable         0.3.6   2024-10-25 [1] CRAN (R 4.5.0)
 htmltools      0.5.8.1 2024-04-04 [1] CRAN (R 4.5.0)
 htmlwidgets    1.6.4   2023-12-06 [1] CRAN (R 4.5.0)
 jsonlite       2.0.0   2025-03-27 [1] CRAN (R 4.5.0)
 knitr          1.50    2025-03-16 [1] CRAN (R 4.5.0)
 labeling       0.4.3   2023-08-29 [1] CRAN (R 4.5.0)
 lifecycle      1.0.4   2023-11-07 [1] CRAN (R 4.5.0)
 magrittr       2.0.4   2025-09-12 [1] CRAN (R 4.5.1)
 pillar         1.11.1  2025-09-17 [1] CRAN (R 4.5.0)
 pkgconfig      2.0.3   2019-09-22 [1] CRAN (R 4.5.0)
 R6             2.6.1   2025-02-15 [1] CRAN (R 4.5.0)
 ragg         * 1.5.0   2025-09-02 [1] CRAN (R 4.5.1)
 RColorBrewer   1.1-3   2022-04-03 [1] CRAN (R 4.5.0)
 rlang          1.1.6   2025-04-11 [1] CRAN (R 4.5.0)
 rmarkdown      2.30    2025-09-28 [1] CRAN (R 4.5.0)
 rstudioapi     0.17.1  2024-10-22 [1] CRAN (R 4.5.0)
 S7             0.2.0   2024-11-07 [1] CRAN (R 4.5.0)
 scales         1.4.0   2025-04-24 [1] CRAN (R 4.5.0)
 sessioninfo    1.2.3   2025-02-05 [1] CRAN (R 4.5.0)
 systemfonts  * 1.3.1   2025-10-01 [1] CRAN (R 4.5.1)
 textshaping    1.0.4   2025-10-10 [1] CRAN (R 4.5.0)
 tibble       * 3.3.0   2025-06-08 [1] CRAN (R 4.5.0)
 tidyselect     1.2.1   2024-03-11 [1] CRAN (R 4.5.0)
 utf8           1.2.6   2025-06-08 [1] CRAN (R 4.5.0)
 vctrs          0.6.5   2023-12-01 [1] CRAN (R 4.5.0)
 withr          3.0.2   2024-10-28 [1] CRAN (R 4.5.0)
 xfun           0.54    2025-10-30 [1] CRAN (R 4.5.0)
 yaml           2.3.10  2024-07-26 [1] CRAN (R 4.5.0)

 [1] /Users/stadler/Library/R/arm64/4.5/library/__bioc322
 [2] /Library/Frameworks/R.framework/Versions/4.5-arm64/Resources/library
 * ── Packages attached to the search path.

──────────────────────────────────────────────────────────────────────────────