Interactive table with images and charts



Interactive table made in R with reactable that has images and charts in it.
This blogpost guides you through the construction of this table made by Tanya Shapiro.

Table section Data to Viz

About


This page showcases the work of Tanya Shapiro. Thanks to her for agreeing to share her work here!

The table uses the reactable package to create an interactive table on the best female tennis player and their titles. It uses Wikipedia data and has the following features on each row:

Load packages


Let’s start by loading the packages needed to build the table:

library(tidyverse)
library(reactablefmtr)
library(reactable)
library(htmltools)
library(sysfonts)
library(showtext)
library(webshot2)


Load and prepare the dataset


Today’s data can be easily accessed on the github of the gallery by using the following code:

# REMOVE WHEN MERGING
# url = 'https://raw.githubusercontent.com/holtzy/R-graph-gallery/master/DATA/tennis-best-players.csv'
# df = readRDS(url)
df = read.csv2("DATA/tennis-best-players.csv", sep=',')


Create the table


Now let’s create the table:

# create custom color palette for scale fill
pal_scale <- c("#F4FFFD", "#E9DAEC", "#A270E5", "#43009A")

# main body of reactable - note, I downloaded the sans-serif font locally from Google Fonts first!
table <- reactable(df %>% select(rank, player, region, australian_open, french_open, us_open, wimbledon, titles),
  theme = reactableTheme(
    style = list(fontFamily = "sans-serif"),
    borderColor = "#DADADA"
  ),
  defaultPageSize = 11,
  defaultColDef = colDef(
    vAlign = "center",
    align = "center",
    headerVAlign = "center",
    style = color_scales(df, span = 4:7, colors = pal_scale),
    headerStyle = list(fontFamily = "sans-serif"),
    width = 90
  ),
  columnGroups = list(
    colGroup(name = "", columns = c("player", "region", "titles"), headerStyle = list(fontFamily = "sans-serif"), align = "left"),
    colGroup(name = "Event", columns = c("australian_open", "us_open", "french_open", "wimbledon"), headerStyle = list(fontFamily = "Roboto"))
  ),
  columns = list(
    rank = colDef(show = FALSE),
    player = colDef(
      name = "Player (First Title - Last Title)",
      align = "left", width = 250,
      cell = function(value) {
        # image <- img(src = paste0("https://raw.githubusercontent.com/holtzy/R-graph-gallery/main/img/fromTheWeb/",str_replace_all(tolower(value)," ","_"),".png"), style = "height: 33px;", alt = value)
        image <- img(src = paste0("https://raw.githubusercontent.com/tashapiro/tanya-data-viz/main/tennis/images/", str_replace_all(tolower(value), " ", "_"), ".png"), style = "height: 33px;", alt = value)
        tagList(
          div(style = "display: inline-block;vertical-align:middle;width:50px", image),
          div(
            style = "display: inline-block;vertical-align:middle;",
            div(style = "vertical-align:middle;", value),
            div(style = "vertical-align:middle;font-size:8pt;color:#8C8C8C;", paste0("(", df[df$player == value, ]$years), ")")
          )
        )
      }
    ),
    region = colDef(
      name = "Region",
      align = "left",
      cell = function(value, index) {
        image <- img(src = value, style = "width:60px;height:20px;", alt = value)
        player <- df$player[index]
        if (player %in% c("Monica Seles", "Molla Bjurstedt Mallory")) {
          tagList(div(style = "display:inline-block;vertical-align:middle;width:80px", image, "*"))
        } else {
          tagList(div(style = "display:inline-block;vertical-align:middle;width:50px", image))
        }
      },
      width = 120
    ),
    australian_open = colDef(name = "AU Open"),
    french_open = colDef(name = "FR Open"),
    us_open = colDef(name = "US Open"),
    wimbledon = colDef(name = "Wmbl"),
    titles = colDef(
      name = "Total Titles",
      width = 180,
      class = "border-left",
      align = "left",
      cell = data_bars(df,
        fill_color = "#7814ff",
        text_position = "outside-end",
        bar_height = 10,
        text_size = 12,
        min_value = 5,
        max_value = 32,
        background = "transparent"
      )
    )
  )
)



# add title, subtitle, footnote and source
# note, I downloaded fonts locally - sans-serif & Font Awesome Branded Icons
table_final <- table %>%
  # title & subtitle
  htmlwidgets::prependContent(
    tagList( # get tennis logo for the headr
      tags$img(src = "https://pngimg.com/uploads/tennis/tennis_PNG10416.png", style = "width:50px;height:34px;display:inline-block;vertical-align:middle;"),
      # tags$h1("trophy  ",style="font-family:'Font Awesome 6 Free';margin-bottom:0;display:inline-block;vertical-align:middle;padding-right:10px;"),
      tags$div("Grand Slam Legends", style = "font-size:32px;font-weight:bold;font-family:sans-serif;margin-bottom:0;display:inline-block;vertical-align:middle;"),
      tags$h3("Top Women's Tennis Players by Singles Championship Titles", style = "font-family:sans-serif;margin-bottom:0;margin-top:0;font-weight:400;color:#8C8C8C;padding-left:10px;")
    )
  ) %>%
  # footnote and source
  htmlwidgets::appendContent(
    tags$div("* Player represented more than one country during career. Most recent country shown.", style = "font-family:Roboto;color:black;font-size:9pt;border-bottom-style:solid;border-top-style:solid;width:910px;padding-bottom:8px;padding-top:8px;border-color:#DADADA;"),
    tags$div(
      tags$div("Data: Wikipedia as of November 2022 | Graphic: ", style = "display:inline-block;vertical-align:middle;"),
      tags$div("twitter", style = "font-family:'Font Awesome 6 Brands';display:inline-block;vertical-align:middle;"),
      tags$div("tanya_shapiro", style = "display:inline-block;vertical-align:middle;"),
      tags$div("github", style = "font-family:'Font Awesome 6 Brands';display:inline-block;vertical-align:middle;"),
      tags$div("tashapiro", style = "display:inline-block;vertical-align:middle;"),
      style = "font-family:sans-serif;color:#8C8C8C;font-size:10pt;width:910px;padding-top:8px;display:inline-block;vertical-align:middle;"
    )
  )

Here’s what the final result looks like. Try clicking on a column title to change the row order automatically!

Conclusion

This post explains how to reproduce an interactive table built with reactable by Tanya Shapiro. The table has very nice features such as clickable columns, gradient colors and inline charts.

If you want to learn more about reactable, check out the dedicated section. Check also the table section


Related chart types


Barplot
Spider / Radar
Wordcloud
Parallel
Lollipop
Circular Barplot



Contact

This document is a work by Yan Holtz. Any feedback is highly encouraged. You can fill an issue on Github, drop me a message on Twitter, or send an email pasting yan.holtz.data with gmail.com.

Github Twitter