Skip to contents

Overview

A high-level overview of the functions and styling customization options available in {reactablefmtr}.

Titles and Sources

A title, subtitle, and source can be added to any reactable table using add_title(), add_subtitle(), and add_source() respectively. There are several built-in formatters available, such as the ability to change the font size, font color, font style, and margin.

library(palmerpenguins)

reactable(penguins) %>% 
  add_title(
    title = 'Palmer Penguins'
  ) %>% 
  add_subtitle(
    subtitle = 'Palmer Archipelago (Antarctica) penguin data',
    font_size = 20,
    font_color = '#666666',
    margin = reactablefmtr::margin(t=10,r=0,b=15,l=0)
  ) %>% 
  add_source(
    source = 'Authors: Allison Marie Horst, Alison Presmanes Hill, and Kristen B Gorman',
    font_style = 'italic',
    font_weight = 'bold'
  )

Palmer Penguins

Palmer Archipelago (Antarctica) penguin data

Authors: Allison Marie Horst, Alison Presmanes Hill, and Kristen B Gorman


Inserting images and icons

You can also parse HTML with reactablefmtr::html(). This allows for the ability to add images and clickable links within the title and source as shown below:

reactable(penguins) %>% 
  add_title(
    title = reactablefmtr::html("Palmer Penguins <img src='https://raw.githubusercontent.com/allisonhorst/palmerpenguins/master/man/figures/lter_penguins.png' alt='Palmer Penguins' width='200' height='110'>")
  ) %>% 
  add_subtitle(
    subtitle = 'Palmer Archipelago (Antarctica) penguin data',
    font_size = 20,
    font_color = '#666666',
    margin = reactablefmtr::margin(t=10,r=0,b=15,l=0)
  ) %>% 
  add_source(
    source = reactablefmtr::html("<i class='fas fa-book'></i> Authors: Allison Marie Horst, Alison Presmanes Hill, and Kristen B Gorman <br> <i class='fas fa-palette'></i> Artwork by @allison_horst "),
    font_style = 'italic',
    font_weight = 'bold'
  ) %>% 
  add_source(
    source = html("<i class='fas fa-link'></i> Link to package: <a href='https://allisonhorst.github.io/palmerpenguins/'>{palmerpenguins}</a>"),
    font_style = 'italic',
    font_weight = 'bold'
  )

Palmer Penguins Palmer Penguins

Palmer Archipelago (Antarctica) penguin data

Authors: Allison Marie Horst, Alison Presmanes Hill, and Kristen B Gorman
Artwork by @allison_horst

Link to package: {palmerpenguins}


Themes

There are over 20 themes available within {reactablefmtr} that can be applied simply within reactable::theme.

There are additional styling options within each theme that allows you to change the font color and font size of both the body of the table and the header, as shown below in the NY Times-inspired table:

Another important feature within each theme is the ability to vertically center the values within each row. By default, {reactable} tables display the values towards the top of each cell. To center the values, include centered = TRUE within the theme options:

FiveThirtyEight

penguins %>% 
  reactable(theme = fivethirtyeight()) 

New York Times

penguins %>% 
  reactable(theme = nytimes()) 

Dark

penguins %>% 
  reactable(theme = dark())

Bootsrap Themes

penguins %>% 
  reactable(theme = superhero()) 

Bubble grids

Bubble grid charts can easily be created with the bubble_grid() function:

penguins %>%
  group_by(species, sex) %>%
  summarize(across(where(is.numeric), mean, na.rm = TRUE)) %>%
  select(-year) %>%
  reactable(
    defaultColDef = colDef(
      align = 'center',
      cell = bubble_grid(
        data = .,
        number_fmt = scales::comma
      )
    )
  )

Adjust size of bubbles

Adjust the size of the bubbles using min_value and/or max_value:

# load bee colony stressor dataset from tidy tuesday 
stressor <- readr::read_csv('https://raw.githubusercontent.com/rfordatascience/tidytuesday/master/data/2022/2022-01-11/stressor.csv')

stressor %>% 
  filter(year == 2020 & state %in% c('United States','California','Georgia','Texas','Idaho')) %>%
  group_by(state, stressor) %>% 
  summarize(stress_pct = mean(stress_pct, na.rm = TRUE)) %>% 
  pivot_wider(names_from = stressor, values_from = stress_pct) %>% 
  select(c(State = state, Diseases = Disesases, Pesticides, `Other pests/parasites`, Other, Unknown)) %>%
  reactable(
    theme = no_lines(centered = TRUE),
    defaultColDef = colDef(align = 'center'),
    columns = list(
      Diseases = colDef(
        cell = bubble_grid(
          data = .,
          colors = '#084C61',
          number_fmt = scales::number,
          min_value = 0,
          max_value = 15
        )
      ), 
      Pesticides = colDef(
        cell = bubble_grid(
          data = .,
          colors = '#DB504A',
          number_fmt = scales::number,
          min_value = 0,
          max_value = 15
        )
      ), 
      `Other pests/parasites` = colDef(
        cell = bubble_grid(
          data = .,
          colors = '#E3B505',
          number_fmt = scales::number,
          min_value = 0,
          max_value = 15
        )
      ),
      Other = colDef(
        cell = bubble_grid(
          data = .,
          colors = '#4F6D7A',
          number_fmt = scales::number,
          min_value = 0,
          max_value = 15
        )
      ), 
      Unknown = colDef(
        cell = bubble_grid(
          data = .,
          colors = '#56A3A6',
          number_fmt = scales::number,
          min_value = 0,
          max_value = 15
        )
      )
    )
  ) %>% 
  add_title(
    title = reactablefmtr::html("Bee Colony Health Stressors <img src='https://svgsilh.com/svg/30649.svg' alt='Bee' width='40' height='40'>"),
    margin = reactablefmtr::margin(t=0,r=0,b=3,l=0)
    ) %>% 
  add_subtitle(
    subtitle = '% of colonies affected by stressors during 2020',
    font_weight = 'normal',
    font_size = 20,
    margin = reactablefmtr::margin(t=0,r=0,b=6,l=0)
    ) %>% 
  add_source(
    source = 'Data: USDA, #TidyTuesday Week 2, 2022',
    margin = reactablefmtr::margin(t=7,r=0,b=0,l=0),
    font_style = "italic"
    )

Bee Colony Health Stressors Bee

% of colonies affected by stressors during 2020

Data: USDA, #TidyTuesday Week 2, 2022

Note that the animation on sort disappears when the bubbles are all the same color in each column. This is because, by default, the color of the bubble is the only element set to animate on sort, which is set by animation = background 1s ease'. If you still want to see some sort of animation, you could set the animation property to ‘all 1s ease’ as shown below:

If you’re interested in learning more about animation options, please see the CSS transition property documentation here.

Bubble squares

In addition to bubble circles, you can also create bubble squares by setting the shape parameter to ‘squares’:

penguins %>%
  group_by(species, sex) %>%
  summarize(across(where(is.numeric), mean, na.rm = TRUE)) %>%
  select(-year) %>%
  reactable(
    theme = no_lines(centered = TRUE),
    defaultColDef = colDef(align = 'center'),
    columns = list(
      bill_length_mm = colDef(
        cell = bubble_grid(
          data = .,
          shape = 'squares',
          colors = c('#f2f0f7','#cbc9e2','#9e9ac8','#756bb1','#54278f'),
          box_shadow = TRUE,
          number_fmt = scales::number_format(accuracy = 0.1),
          min_value = 30,
          animation = 'all 1s ease'
        )
      ),
      bill_depth_mm = colDef(
        cell = bubble_grid(
          data = .,
          shape = 'squares',
          colors = c('#feedde','#fdbe85','#fd8d3c','#e6550d','#a63603'),
          box_shadow = TRUE,
          number_fmt = scales::number_format(accuracy = 0.1),
          min_value = 13,
          max_value = 21,
          animation = 'all 1s ease'
        )
      ),
      flipper_length_mm = colDef(
        cell = bubble_grid(
          data = .,
          shape = 'squares',
          colors = c('#f1eef6','#bdc9e1','#74a9cf','#2b8cbe','#045a8d'),
          box_shadow = TRUE,
          number_fmt = scales::number_format(accuracy = 0.1),
          min_value = 120,
          animation = 'all 1s ease'
        )
      ),
      body_mass_g = colDef(
        cell = bubble_grid(
          data = .,
          shape = 'squares',
          colors = c('#edf8e9','#bae4b3','#74c476','#31a354','#006d2c'),
          opacity = 0.8,
          box_shadow = TRUE,
          number_fmt = scales::comma,
          min_value = 2000,
          animation = 'all 1s ease'
        )
      )
    )
  )

Bar charts

There are many ways to customize the appearance of the bar charts within data_bars():

penguins %>% 
  group_by(species, island, sex) %>% 
  summarize(across(where(is.numeric), mean, na.rm = TRUE)) %>% 
  select(-year) %>% 
  reactable(
    theme = clean(),
    pagination = FALSE,
    columns = list(
      bill_length_mm = colDef(
        cell = data_bars(
          data = .,
          fill_color = viridis::mako(5),
          background = '#F1F1F1',
          min_value = 35,
          max_value = 55,
          text_position = 'outside-end',
          number_fmt = scales::comma
        )
      ),
      bill_depth_mm = colDef(
        cell = data_bars(
          data = .,
          fill_color = c('#FFF2D9','#FFE1A6','#FFCB66','#FFB627'),
          fill_gradient = TRUE,
          background = 'transparent',
          number_fmt = scales::comma_format(accuracy = 0.1)
        )
      ),
      flipper_length_mm = colDef(
        cell = data_bars(
          data = .,
          fill_color = 'black',
          fill_opacity = 0.8,
          round_edges = TRUE,
          text_position = 'center',
          number_fmt = scales::comma
        )
      ),
      body_mass_g = colDef(
        cell = data_bars(
          data = .,
          fill_color = 'white',
          background = 'darkgrey',
          border_style = 'solid',
          border_width = '1px',
          border_color = 'forestgreen',
          box_shadow = TRUE,
          text_position = 'inside-base',
          number_fmt = scales::comma
        )
      )
    )
  )

Conditional fill

Conditionally assign colors to bars by assigning the colors within a new column using dplyr::case_when() and then referencing that column to be used as the fill of the bars with fill_color_ref:

survey <- data.frame(
  response = c('Agree','Disagree','Neutral'),
  percentage = c(0.64, 0.29, 0.07)
)

survey %>% 
  mutate(response_colors = case_when(
    response == 'Agree' ~ '#127852',
    response == 'Disagree' ~ '#C40233',
    TRUE ~ '#A5A0A1'
  )) %>% 
  reactable(
    theme = clean(centered = TRUE),
    columns = list(
      response_colors = colDef(show = FALSE),
      response = colDef(maxWidth = 110),
      percentage = colDef(
        align = 'left',
        cell = data_bars(
          data = .,
          fill_color_ref = 'response_colors',
          number_fmt = scales::percent,
          max_value = 1,
          bar_height = 50,
          text_size = '1.3em'
        )
      )
    )
  )

Fill by another column

Alternatively, you can use fill_by to apply a bar chart to a column containing text:

survey %>%
  mutate(response_colors = case_when(
    response == 'Agree' ~ '#127852',
    response == 'Disagree' ~ '#C40233',
    TRUE ~ '#8C8687'
  ),
  response = paste0(response, " (", percentage*100, "%)")) %>%
  reactable(
    theme = void(centered = TRUE),
    columns = list(
      response = colDef(
        name = '',
        cell = data_bars(
          data = .,
          fill_by = 'percentage',
          fill_color_ref = 'response_colors',
          text_position = 'outside-end',
          max_value = 1,
          bar_height = 50,
          text_color_ref = 'response_colors',
          text_size = '1.5em',
          bold_text = TRUE
        )
      ),
      response_colors = colDef(show = FALSE),
      percentage = colDef(show = FALSE)
    )
  ) %>%
  add_title('Survey Responses')

Survey Responses

Add icons to bars

If you would like to add an icon to the data bars, you can do so by providing the name of the icon within the icon parameter.

Note that the color of the icon will automatically be inherited by the color of the data bar unless provided explicity within the icon_color parameter.

cars <- mtcars %>%
  rownames_to_column(var = 'model') %>% 
  select(c(model,mpg,hp)) 

reactable(cars,
  theme = nytimes(centered = TRUE),
  compact = TRUE,
  defaultSortOrder = 'desc',
  defaultSorted = 'hp',
  pagination = FALSE,
  columns = list(
    hp = colDef(
      minWidth = 150,
      cell = data_bars(
        data = cars,
        text_position = 'none',
        box_shadow = TRUE,
        round_edges = TRUE,
        fill_color = rev(MetBrewer::met.brewer('Troy')),
        bias = 1.5,
        icon = 'horse-head',
        background = 'transparent',
        bar_height = 4,
        max_value = 400
      )
    ),
    mpg = colDef(
      minWidth = 150,
      cell = data_bars(
        data = cars,
        text_position = 'none',
        box_shadow = TRUE,
        round_edges = TRUE,
        fill_color = MetBrewer::met.brewer('VanGogh3'),
        bias = 1.5,
        icon = 'leaf',
        background = 'transparent',
        bar_height = 4
      )
    )
  )
) 

Adding images to bars

To add an external web image to the data bars, you can do so by providing a link to the image within the img parameter:

cars <- mtcars %>%
  rownames_to_column(var = 'model') %>%
  select(c(model,mpg,cyl,drat,wt,hp,qsec)) %>%
  mutate(rank = rank(qsec)) %>%
  relocate(rank, .before = 'model')

reactable(cars,
  theme = nytimes(centered = TRUE, font_color = '#666666'),
  compact = TRUE,
  defaultSortOrder = 'asc',
  defaultSorted = 'qsec',
  pagination = FALSE,
  showSortIcon = FALSE,
  columns = list(
    model = colDef(
      minWidth = 100,
      style = cell_style(
        data = cars,
        font_color = '#222222',
        font_weight = 'bold'
      )),
    mpg = colDef(maxWidth = 60),
    cyl = colDef(maxWidth = 60),
    hp = colDef(maxWidth = 60),
    drat = colDef(maxWidth = 60),
    wt = colDef(maxWidth = 60),
    rank = colDef(
      maxWidth = 45,
      format = colFormat(digits = 0)
    ),
    qsec = colDef(
      name = '1/4 mile time',
      align = 'left',
      minWidth = 200,
      format = colFormat(digits = 0),
      cell = data_bars(
        data = cars,
        fill_color = c('#FAFAFA','#E7E7E7','#D3D3D3','#BFBFBF'),
        fill_gradient = TRUE,
        bold_text = TRUE,
        background = 'transparent',
        text_position = 'inside-base',
        text_color = '#222222',
        number_fmt = scales::number_format(accuracy = 0.1, suffix = 's'),
        bar_height = 12,
        min_value = 13,
        max_value = 25,
        img = 'https://www.pngkit.com/png/detail/54-544889_45-top-view-of-car-clipart-images-racecar.png',
        img_height = 20,
        img_width = 25
      )
    )
  )
) %>%
  add_title(
    title = reactablefmtr::html("Fastest cars in the mtcars dataset <i class='fas fa-flag-checkered'></i>"),
    font_color = 'black',
    text_shadow = '1px 1px 2px red',
    margin = margin(t=0,r=0,b=5,l=0)
  )

Fastest cars in the mtcars dataset

Display positive and negative values

Data bars are able to display both positive and negative values automatically:

cars <- mtcars %>%
  rownames_to_column(var = 'model') %>% 
  select(c(model,wt,mpg,hp)) %>% 
  mutate(wt = wt-mean(wt),
         mpg = mpg-mean(mpg),
         hp = hp-mean(hp))

reactable(cars,
  theme = nytimes(centered = TRUE),
  compact = TRUE,
  defaultSortOrder = 'desc',
  defaultSorted = 'mpg',
  pagination = FALSE,
  columns = list(
    wt = colDef(
      name = 'WT VS AVG',
      minWidth = 150,
      align = 'center',
      cell = data_bars(
        data = cars,
        text_position = 'outside-end',
        fill_color = viridis::mako(5),
        number_fmt = scales::number_format(accuracy = 0.01)
      )
    ),
    hp = colDef(
      name = 'HP VS AVG',
      minWidth = 150,
      align = 'center',
      cell = data_bars(
        data = cars,
        text_position = 'outside-end',
        fill_color = c('#C40233','#127852'),
        number_fmt = scales::comma
      )
    ),
    mpg = colDef(
      name = 'MPG VS AVG',
      minWidth = 150,
      align = 'center',
      cell = data_bars(
        data = cars,
        text_position = 'none',
        box_shadow = TRUE,
        fill_color = MetBrewer::met.brewer('VanGogh3'),
        number_fmt = scales::comma
      )
    )
  )
) 

Dot plots

You can create dot plot charts by assigning a ‘circle’ icon to the data bars and making the data bars transparent:

midwest %>% 
  group_by(state, county) %>% 
  summarize(perchsd = mean(perchsd/100, na.rm = TRUE),
            percollege = mean(percollege/100, na.rm = TRUE)) %>% 
  filter(state == 'IL') %>% 
  reactable(
  theme = clean(),
  defaultSorted = 'county',
  defaultPageSize = 25,
  paginationType = 'jump',
  columns = list(
    state = colDef(maxWidth = 80),
    county = colDef(maxWidth = 120),
    perchsd = colDef(
      name = '% with a High School Diploma',
      align = 'left',
      minWidth = 250,
      cell = data_bars(
        data = .,
        fill_color = '#EEEEEE',
        number_fmt = scales::percent,
        text_position = 'outside-end',
        max_value = 1,
        icon = 'circle',
        icon_color = 'firebrick',
        icon_size = 15,
        text_color = 'firebrick',
        round_edges = TRUE
      )
    ),
    percollege = colDef(
      name = '% with a College Education',
      align = 'left',
      minWidth = 250,
      cell = data_bars(
        data = .,
        fill_color = '#EEEEEE',
        number_fmt = scales::percent,
        text_position = 'outside-end',
        max_value = 1,
        icon = 'circle',
        icon_color = '#226ab2',
        icon_size = 15,
        text_color = '#226ab2',
        round_edges = TRUE
      )
    )
  )
)

Lollipop charts

You can create lollipop charts by following a similar method used to create the dot plot charts above, but by adding color to the data bars and reducing the height of the bars:

midwest %>% 
  group_by(state, county) %>% 
  summarize(perchsd = mean(perchsd/100, na.rm = TRUE),
            percollege = mean(percollege/100, na.rm = TRUE)) %>% 
  filter(state == 'IL') %>% 
  reactable(
  theme = clean(),
  defaultSorted = 'county',
  defaultPageSize = 25,
  paginationType = 'jump',
  columns = list(
    state = colDef(maxWidth = 80),
    county = colDef(maxWidth = 120),
    perchsd = colDef(
      name = '% with a High School Diploma',
      align = 'left',
      minWidth = 250,
      cell = data_bars(
        data = .,
        fill_color = 'firebrick',
        background = '#FFFFFF',
        bar_height = 7,
        number_fmt = scales::percent,
        text_position = 'outside-end',
        max_value = 1,
        icon = 'circle',
        icon_color = 'firebrick',
        icon_size = 15,
        text_color = 'firebrick',
        round_edges = TRUE
      )
    ),
    percollege = colDef(
      name = '% with a College Education',
      align = 'left',
      minWidth = 250,
      cell = data_bars(
        data = .,
        fill_color = '#226ab2',
        background = '#FFFFFF',
        bar_height = 7,
        number_fmt = scales::percent,
        text_position = 'outside-end',
        max_value = 1,
        icon = 'circle',
        icon_color = '#226ab2',
        icon_size = 15,
        text_color = '#226ab2',
        round_edges = TRUE
      )
    )
  )
)

Heatmaps

Heatmaps can be created by using the color_scales() function and hiding the text by setting show_text to FALSE. Additionally, you can set tooltip to TRUE so that you can still see the values when a user hovers over each cell:

sanmarcos_sales <- txhousing %>% 
  filter(city == 'San Marcos' & year > 2004 & year < 2015) %>%
  group_by(year, month) %>% 
  summarize(sales = mean(sales, na.rm = TRUE)) %>%
  mutate(month = month.abb[month]) %>% 
  pivot_wider(names_from = 'month', values_from = 'sales') %>% 
  ungroup() %>% 
  mutate(year = as.character(year),
         total = rowSums(across(where(is.numeric))))

sanmarcos_legend <- txhousing %>% 
  filter(city == 'San Marcos' & year > 2004 & year < 2015) %>%
  group_by(year, month) %>% 
  summarize(sales = mean(sales, na.rm = TRUE)) %>%
  mutate(month = month.abb[month]) 

reactable(
  sanmarcos_sales,
  pagination = FALSE,
  showSortIcon = FALSE,
  theme = void(
    centered = TRUE, 
    cell_padding = 0,
    header_font_color = 'black',
    font_color = 'black'
    ),
  defaultColDef = colDef(
    maxWidth = 50,
    align = 'center',
    cell = tooltip(),
    style = color_scales(
      data = sanmarcos_sales,
      span = 2:13,
      colors = c('#002347','#003366','#003F7D','#FF8E00','#FD7702','#FF5003'),
      bias = 1.4,
      opacity = 0.9,
      show_text = FALSE
    )
  ),
  columns = list(
    total = colDef(
      maxWidth = 225,
      cell = data_bars(
        data = sanmarcos_sales,
        fill_color = c('#002347','#003366','#003F7D','#FF8E00','#FD7702','#FF5003'),
        bias = 1.4,
        fill_opacity = 0.9,
        background = 'transparent',
        bar_height = 40,
        text_position = 'center'
        ),
      style = list(borderLeft = "2px solid #999999")
    )
  )
) %>% 
  add_title(
    title = html("San Marcos Housing Sales <i class='fas fa-home'></i>"),
    align = 'center',
    margin = reactablefmtr::margin(t=10,r=0,b=2,l=0)
  ) %>% 
  add_subtitle(
    subtitle = 'Hover over cells to see values',
    font_style = 'italic',
    font_color = '#777777',
    font_size = 18,
    align = 'center',
    margin = reactablefmtr::margin(t=0,r=0,b=10,l=0)
  ) %>% 
  add_legend(
    data = sanmarcos_legend,
    align = 'left',
    title = '# of Sales (Jan - Dec)',
    col_name = 'sales',
    colors = c('#002347','#003366','#003F7D','#FF8E00','#FD7702','#FF5003'),
    bias = 1.4,
    bins = 6
  )

San Marcos Housing Sales

Hover over cells to see values

# of Sales (Jan - Dec)
  • 6
  • 14
  • 19
  • 26
  • 34
  • 63


Nested tables

{reactablefmtr} styling supports nested {reactable} tables that expand on click:

data <- MASS::Cars93[1:30, c('Type','Make','Model','MPG.city','MPG.highway')]

averages <- data %>%
  group_by(Type) %>%
  summarize(
    MPG.city = mean(MPG.city),
    MPG.highway = mean(MPG.highway)
  )

reactable(
  averages,
  theme = clean(centered = TRUE),
  columns = list(
    Type = colDef(maxWidth = 250),
    MPG.city = colDef(
      maxWidth = 200, 
      style = color_scales(
        data = data, 
        colors = viridis::mako(5)), 
      format = colFormat(digits = 1)),
    MPG.highway = colDef(
      maxWidth = 200, 
      cell = data_bars(
        data = data, 
        fill_color = viridis::mako(5), 
        number_fmt = scales::comma))
    ),
  onClick = "expand",
  details = function(index) {
    data_sub <- data[data$Type == averages$Type[index], ]
    reactable(
      data_sub,
      theme = clean(centered = TRUE),
      columns = list(
        Type = colDef(show = FALSE),
        Make = colDef(maxWidth = 175),
        Model = colDef(maxWidth = 120),
        MPG.city = colDef(
          maxWidth = 200, 
          style = color_scales(data, viridis::mako(5)), 
          format = colFormat(digits = 1)),
        MPG.highway = colDef(
          maxWidth = 200, 
          cell = data_bars(data, fill_color = viridis::mako(5), number_fmt = scales::comma))
        )
    ) 
  }
)