Better tables with {gt}

Quick tutorial: gt with the palmerpenguins dataset

This short tutorial shows common gt workflows: build a basic table, format numbers and labels, add color scales, and produce a grouped summary.

Setup

Install and load packages (only once):

# install.packages(c("gt", "palmerpenguins", "dplyr", "scales"))
library(tidyverse)
── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
✔ dplyr     1.1.4     ✔ readr     2.1.5
✔ forcats   1.0.0     ✔ stringr   1.5.1
✔ ggplot2   3.5.2     ✔ tibble    3.3.0
✔ lubridate 1.9.4     ✔ tidyr     1.3.1
✔ purrr     1.0.4     
── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag()    masks stats::lag()
ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
library(gt)
library(palmerpenguins)

Attaching package: 'palmerpenguins'

The following objects are masked from 'package:datasets':

    penguins, penguins_raw
library(scales)

Attaching package: 'scales'

The following object is masked from 'package:purrr':

    discard

The following object is masked from 'package:readr':

    col_factor
  1. Basic table Select a handful of columns and render a simple gt table.
penguins_clean <- penguins %>%
  select(species, island, bill_length_mm, bill_depth_mm, flipper_length_mm, body_mass_g, sex) %>%
  drop_na()

penguins_clean %>%
  slice_head(n = 10) %>%
  gt()
species island bill_length_mm bill_depth_mm flipper_length_mm body_mass_g sex
Adelie Torgersen 39.1 18.7 181 3750 male
Adelie Torgersen 39.5 17.4 186 3800 female
Adelie Torgersen 40.3 18.0 195 3250 female
Adelie Torgersen 36.7 19.3 193 3450 female
Adelie Torgersen 39.3 20.6 190 3650 male
Adelie Torgersen 38.9 17.8 181 3625 female
Adelie Torgersen 39.2 19.6 195 4675 male
Adelie Torgersen 41.1 17.6 182 3200 female
Adelie Torgersen 38.6 21.2 191 3800 male
Adelie Torgersen 34.6 21.1 198 4400 male
  1. Format numbers and labels Use gt formatting helpers to control decimal places and column labels.
penguins_clean %>%
  slice_head(n = 10) %>%
  gt() %>%
  cols_label(
    species = "Species",
    island = "Island",
    bill_length_mm = "Bill (mm)",
    bill_depth_mm  = "Bill depth (mm)",
    flipper_length_mm = "Flipper (mm)",
    body_mass_g = "Body mass (g)",
    sex = "Sex"
  ) %>%
  fmt_number(
    columns = c("bill_length_mm", "bill_depth_mm", "flipper_length_mm"),
    decimals = 1
  ) %>%
  fmt_number(
    columns = c("body_mass_g"),
    decimals = 0
  )
Species Island Bill (mm) Bill depth (mm) Flipper (mm) Body mass (g) Sex
Adelie Torgersen 39.1 18.7 181.0 3,750 male
Adelie Torgersen 39.5 17.4 186.0 3,800 female
Adelie Torgersen 40.3 18.0 195.0 3,250 female
Adelie Torgersen 36.7 19.3 193.0 3,450 female
Adelie Torgersen 39.3 20.6 190.0 3,650 male
Adelie Torgersen 38.9 17.8 181.0 3,625 female
Adelie Torgersen 39.2 19.6 195.0 4,675 male
Adelie Torgersen 41.1 17.6 182.0 3,200 female
Adelie Torgersen 38.6 21.2 191.0 3,800 male
Adelie Torgersen 34.6 21.1 198.0 4,400 male
  1. Add a color scale Highlight body mass with a color ramp to make patterns visible.
body_range <- range(penguins_clean$body_mass_g, na.rm = TRUE)

penguins_clean %>%
  slice_head(n = 10) %>%
  gt() %>%
  cols_label(body_mass_g = "Body mass (g)") %>%
  data_color(
    columns = c("body_mass_g"),
    colors = col_numeric(
      palette = c("lightyellow", "orange", "red"),
      domain = body_range
    )
  ) %>%
  fmt_number(columns = vars(body_mass_g), decimals = 0)
Warning: Since gt v0.9.0, the `colors` argument has been deprecated.
• Please use the `fn` argument instead.
This warning is displayed once every 8 hours.
Warning: Since gt v0.3.0, `columns = vars(...)` has been deprecated.
• Please use `columns = c(...)` instead.
species island bill_length_mm bill_depth_mm flipper_length_mm Body mass (g) sex
Adelie Torgersen 39.1 18.7 181 3,750 male
Adelie Torgersen 39.5 17.4 186 3,800 female
Adelie Torgersen 40.3 18.0 195 3,250 female
Adelie Torgersen 36.7 19.3 193 3,450 female
Adelie Torgersen 39.3 20.6 190 3,650 male
Adelie Torgersen 38.9 17.8 181 3,625 female
Adelie Torgersen 39.2 19.6 195 4,675 male
Adelie Torgersen 41.1 17.6 182 3,200 female
Adelie Torgersen 38.6 21.2 191 3,800 male
Adelie Torgersen 34.6 21.1 198 4,400 male

Using scales to create the colour mapper The function col_numeric() used above comes from the scales package (scales::col_numeric). It builds and returns a function that maps numeric values to colors; you pass that function to gt::data_color so gt can colour cells based on the numeric values. The two important arguments are palette (the colour stops — use hex codes or a perceptually uniform palette like viridis) and domain (the numeric range to map).

Tip: construct the mapping function explicitly so you can inspect or reuse it, and prefer explicit domains (range(…, na.rm = TRUE)) and hex colours for reproducibility and accessibility.

# create a reusable colour-mapping function (example using viridis)
library(viridisLite)   # lightweight dependency for perceptual colours

color_fn <- scales::col_numeric(
  palette = viridisLite::viridis(5),                       # perceptually uniform palette
  domain  = range(penguins_clean$body_mass_g, na.rm = TRUE) # explicit numeric domain
)

# inspect output for a few values
color_fn(c(2700, 3500, 5000))
[1] "#440154" "#3E4B85" "#4BAF77"
# use the function with gt::data_color
penguins_clean %>%
  slice_head(n = 10) %>%
  gt() %>%
  cols_label(body_mass_g = "Body mass (g)") %>%
  data_color(columns = c("body_mass_g"), colors = color_fn) %>%
  fmt_number(columns = vars(body_mass_g), decimals = 0)
Warning: Since gt v0.3.0, `columns = vars(...)` has been deprecated.
• Please use `columns = c(...)` instead.
species island bill_length_mm bill_depth_mm flipper_length_mm Body mass (g) sex
Adelie Torgersen 39.1 18.7 181 3,750 male
Adelie Torgersen 39.5 17.4 186 3,800 female
Adelie Torgersen 40.3 18.0 195 3,250 female
Adelie Torgersen 36.7 19.3 193 3,450 female
Adelie Torgersen 39.3 20.6 190 3,650 male
Adelie Torgersen 38.9 17.8 181 3,625 female
Adelie Torgersen 39.2 19.6 195 4,675 male
Adelie Torgersen 41.1 17.6 182 3,200 female
Adelie Torgersen 38.6 21.2 191 3,800 male
Adelie Torgersen 34.6 21.1 198 4,400 male

For other needs, scales also provides helpers like col_bin(), col_quantile(), and col_factor() to create binned, quantile-based, or categorical colour mappings. ``````

  1. Grouped summary (per species) Create a summary table grouped by species: mean body mass and median flipper length.
summary_tbl <- penguins_clean %>%
  group_by(species) %>%
  summarise(
    n = n(),
    mean_body_mass = mean(body_mass_g, na.rm = TRUE),
    median_flipper = median(flipper_length_mm, na.rm = TRUE)
  ) %>%
  ungroup()

summary_tbl %>%
  gt(rowname_col = "species") %>%
  cols_label(
    n = "Count",
    mean_body_mass = "Mean body mass (g)",
    median_flipper = "Median flipper (mm)"
  ) %>%
  fmt_number(columns = vars(mean_body_mass, median_flipper), decimals = 1)
Warning: Since gt v0.3.0, `columns = vars(...)` has been deprecated.
• Please use `columns = c(...)` instead.
Count Mean body mass (g) Median flipper (mm)
Adelie 146 3,706.2 190.0
Chinstrap 68 3,733.1 196.0
Gentoo 119 5,092.4 216.0
  1. Grouped details plus summary rows Show species groups with a grand summary row.
penguins_clean %>%
  group_by(species) %>%
  slice_sample(n = 6) %>%    # small sample per species for display
  ungroup() %>%
  gt(groupname_col = "species") %>%
  summary_rows(
    groups = TRUE,
    columns = c("body_mass_g"),
    fns = list(
      "avg (g)" = ~mean(., na.rm = TRUE),
      "min (g)" = ~min(., na.rm = TRUE),
      "max (g)" = ~max(., na.rm = TRUE)
    )
  ) %>%
  grand_summary_rows(
    columns = c("body_mass_g"),
    fns = list("Overall avg (g)" = ~mean(., na.rm = TRUE))
  ) %>%
  fmt_number(columns = vars(body_mass_g), decimals = 0)
Warning: Since gt v0.3.0, `columns = vars(...)` has been deprecated.
• Please use `columns = c(...)` instead.
island bill_length_mm bill_depth_mm flipper_length_mm body_mass_g sex
Adelie
Torgersen 42.8 18.5 195 4,250 male
Biscoe 36.5 16.6 181 2,850 female
Dream 37.0 16.9 185 3,000 female
Torgersen 40.3 18.0 195 3,250 female
Dream 35.7 18.0 202 3,550 female
Torgersen 39.0 17.1 191 3,050 female
Chinstrap
Dream 58.0 17.8 181 3,700 female
Dream 52.7 19.8 197 3,725 male
Dream 45.2 17.8 198 3,950 female
Dream 42.5 17.3 187 3,350 female
Dream 42.5 16.7 187 3,350 female
Dream 52.8 20.0 205 4,550 male
Gentoo
Biscoe 43.6 13.9 217 4,900 female
Biscoe 45.2 15.8 215 5,300 male
Biscoe 50.2 14.3 218 5,700 male
Biscoe 50.4 15.3 224 5,550 male
Biscoe 46.5 13.5 210 4,550 female
Biscoe 46.5 14.4 217 4,900 female
Overall avg (g) 4081.944