Build interactive 'reactable' table of US Craft Beer

I recently discovered the reactable R package and I was eager to build an interactive table with it. I found an interesting US state-by-state craft beer dataset and I thought it would be a nice little project to combine the two of these. The idea for this post, therefore, is to build a table that breaks down craft beer production by state, categorizing the beer into ales, lagers and other (cider & mead).

To begin with, I load the libraries required for this post.

# Load libraries required for this post
library(tidyverse)
library(reactable)
library(htmltools)

This craft beer dataset originally appeared as part of the Tidy Tuesday project. The purpose of this post is to focus on creating the interactive reactable, rather than wrangling the data. Therefore, I simply download CSV files from my GitHub repo that I have already pre-processed.

# Get data - count of Ales, Lagers & Other per US state (Longer format)
state_beer_type_counts <- read_csv("https://raw.githubusercontent.com/buckleco/Portfolio_Data/main/state_beer_type_counts.csv")

# Get data - count of Ales, Lagers & Other per US state including totals (Wider format)
final_table_tib <- read.csv("https://raw.githubusercontent.com/buckleco/Portfolio_Data/main/beer_reactable1.csv")

It is necessary to create some functions and set some variables before creating the reactable. Firstly, I make a function to define the bars for the ‘Number of beers’ column. Next, I create a function to return a color based on an input between 0 and 1, using two hexadecimal colors as the beginning and end of the palette range. The last function defines the columns for the ‘Ale’, ‘Lager’ and ‘Other’ categories, including how the background color of the cell is designated.

# Function to define the bar chart to be used in the table
# This function will be called later when the table is being created
beer_count_bar <- function(label, width = "100%", height = "20px",
                           fill = "#363636", background = NULL) {
  bar <- div(style = list(background = fill, width = width, height = height))
  chart <- div(style = list(
    flexGrow = 1, marginLeft = "5px",
    background = background
  ), bar)
  div(style = list(display = "flex", alignItems = "center"), label, chart)
}

# Function for asigning colors for the tile grid part of the table
beer_pal <- function(x) {
  rgb(colorRamp(c("#fce9c7", "#e6a22e"),
    bias = 2
  )(x), maxColorValue = 255)
}

# Get the max and min beer counts for the color palette
# Note, the top 3 counts are removed, as they skew the distribution
# The color of the top 3 is hardcoded later on to be the maximum
min_beer_count <- min(state_beer_type_counts[-1:-3, "n"])
max_beer_count <- max(state_beer_type_counts[-1:-3, "n"])

# Function to define the column for each category of beer
# This will be called later, once each for the Ale, Lager and Other categories
beer_type_column <- function(col, class = NULL, ...) {
  colDef(
    ...,
    maxWidth = 75,
    defaultSortOrder = "desc",
    headerStyle = list(fontSize = "16px", fontWeight = 500),
    style = function(value) { # This function assigns the color of the cell
      pal_adj <- (value - min_beer_count) / (max_beer_count - min_beer_count)
      if (value < 1) {
        list(color = "#aaa", background = "#FFFFFF") # grey text & white background
      } else if (pal_adj > 0.99) {
        list(background = "#f28e1c") # max color level for top 3 Ales
      } else {
        list(background = beer_pal(pal_adj)) # color from palette function
      }
    }
  )
}

# Column names for the grouped columns
# 'Other' are beers that do not fall under 'Ale' or 'Lager'; namely Mead and Cider
beer_type_cols <- c("Ale", "Lager", "Other")

Now the table can be created using the reactable() function from the reactable R package.

# Create the reactable table
final_beer_type_table_states <- reactable(
  data = final_table_tib,
  style = list(
    fontFamily = "Work Sans, sans-serif",
    fontSize = "14px", background = "#FFFFFF"
  ),
  defaultSorted = "Total",
  defaultPageSize = 10,
  defaultColDef = colDef(class = "cell", headerClass = "header"),
  highlight = TRUE,
  searchable = TRUE,
  compact = TRUE,
  columnGroups = list(colGroup(
    name = "Types of beer", columns = beer_type_cols,
    headerStyle = list(fontSize = "14px", fontWeight = 400)
  )),
  columns = list(
    state_name = colDef(
      name = "State",
      minWidth = 120,
      headerStyle = list(fontSize = "16px", fontWeight = 500),
      style = list(fontSize = "14px", fontWeight = 500)
    ),
    Total = colDef(
      name = "Number of beers",
      defaultSortOrder = "desc",
      minWidth = 180,
      headerStyle = list(fontSize = "16px", fontWeight = 500),
      cell = function(value) {
        width <- paste0(value * 100 / max(final_table_tib$Total), "%")
        # The width is baed on the widest number
        value <- format(value, width = 3, justify = "right")
        beer_count_bar(value, width = width) # call function created above
      },
      align = "left",
      style = list(
        fontFamily = "monospace", whiteSpace = "pre",
        fontSize = "14px", fontWeight = 500
      )
    ),
    # Generate the 'Types of beer' columns using the function created above
    Ale = beer_type_column(name = "Ale", col = "Ale"),
    Lager = beer_type_column(name = "Lager", col = "Lager"),
    Other = beer_type_column(name = "Other", col = "Other")
  )
)



The table clearly shows that, in the world of craft beers, ales are more common than lagers, and Colorado leads the way, producing 265 different beers, of which 230 are ales. Wisconsin, however, leads the way when it comes to craft lagers, producing 37 different lagers, more than any other state.

The reactable table provides a smooth interactive experience. The columns can be sorted in both directions and searching for specific states is very responsive. As can be seen from the code above, the creation of the table is highly customizable, as well. I picked 10 rows as the table length, but displaying the whole table is also possible. Addtionally, the colors, headings and in-table charts can all be manipulated. Here is a helpful resource I used while exploring reactable. It contains plenty of examples of different ways the package can be used. This is definitely a package I look forward to using again.

Conor Buckley
Conor Buckley

My interests include data wrangling, using the R tidyverse, and making insightful charts.