Introduction

This markdown will explain how to create a ~silky smooth~ data table using reactable. Pretty sweet, right? (Okay, enough chocolate puns.)

The data is rankings of over 2,500 plain dark chocolate bars and includes country of origin, manufacturer, cocoa percent and more. It was compiled on a website called Flavors of Cacao. It’s unclear who’s behind the ratings, but whoever it is, they left a pretty involved guide to give us insight into their process.

I stumbled across the data on the Tidy Tuesday Github page, which has lots of great datasets to practice on in R.

Let’s get started!

Step 1: Get set up

Make sure you read in your data and run library(reactable), along with whatever other libraries you want.

## ── Attaching packages ─────────────────────────────────────── tidyverse 1.3.1 ──
## ✓ ggplot2 3.3.5     ✓ purrr   0.3.4
## ✓ tibble  3.1.6     ✓ dplyr   1.0.8
## ✓ tidyr   1.1.4     ✓ stringr 1.4.0
## ✓ readr   2.0.2     ✓ forcats 0.5.1
## Warning: package 'dplyr' was built under R version 4.1.2
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## x dplyr::filter() masks stats::filter()
## x dplyr::lag()    masks stats::lag()

Step 2: Choose your columns

There are a lot of columns in this dataset, and not all of them are relevant for my table. So I’m going to select which columns I want and make a new dataframe with only those columns included.

table_chocolate_ratings <-
  chocolate_ratings %>% 
  select(company_manufacturer, country_of_bean_origin, cocoa_percent, most_memorable_characteristics, rating) 

Step 3: Make a basic reactable

This is done with the function reactable(your_data_frame).

reactable(table_chocolate_ratings)

This is an okay table. It’s sortable by default, which is nice — just go to any column header and click the up and down arrows to sort by ascending/descending. (So for example, you could sort it in order of rating from best to worse.) But we can do better!

Step 4: Add some semi-fancy features

Here are a few features I’ll add in:

  • searchable = TRUE makes a search box in the top right
  • resizable = TRUE makes each column resizable by dragging
  • filterable = TRUE adds a filter box to the top of each column
  • defaultPageSize = sets a limit to the number of rows in your table.
  • bordered= TRUE adds borders to the table.
reactable(table_chocolate_ratings, searchable = TRUE, resizable = TRUE, filterable = TRUE, defaultPageSize = 5, bordered = TRUE)

This table is already looking better! But, the headings are ugly. Let’s work on that next.

Step 5: Clean up the headings

reactable(table_chocolate_ratings, searchable = TRUE, resizable = TRUE, filterable = TRUE, defaultPageSize = 5, bordered = TRUE,
          columns = list(
    company_manufacturer = colDef("Company"),
    cocoa_percent = colDef("Cocoa %"),
    country_of_bean_origin = colDef("Country of Bean Origin"),
    most_memorable_characteristics = colDef("Most Memorable Characteristics"),
    rating = colDef("Rating")))

Cool! Let’s add a few more features, for fun.

Step 6: Group by ‘whatever’

groupBy = ‘variable’ allows you to group by whatever variable you choose. Here, I’ll group by company manufacturer, so we can see all of the chocolate bars made by a company in one place.

reactable(table_chocolate_ratings, searchable = TRUE, resizable = TRUE, filterable = TRUE, defaultPageSize = 5, bordered = TRUE,
          columns = list(
    company_manufacturer = colDef("Company"),
    cocoa_percent = colDef("Cocoa %"),
    country_of_bean_origin = colDef("Country of Bean Origin"),
    most_memorable_characteristics = colDef("Most Memorable Characteristics"),
    rating = colDef("Rating")),
    groupBy = 'company_manufacturer')

Sweet! Adding a few final features…

Step 7: Final touches

  • highlight = TRUE highlights a row when you hover over it.
  • compact = TRUE makes the table nice and compact.
reactable(table_chocolate_ratings, searchable = TRUE, resizable = TRUE, filterable = TRUE, defaultPageSize = 5, bordered = TRUE, highlight = TRUE, compact = TRUE,
          columns = list(
    company_manufacturer = colDef("Company"),
    cocoa_percent = colDef("Cocoa %"),
    country_of_bean_origin = colDef("Country of Bean Origin"),
    most_memorable_characteristics = colDef("Most Memorable Characteristics"),
    rating = colDef("Rating")),
    groupBy = 'company_manufacturer')

And we’re done! There is a lot more you can do with these tables, but this is some of the basics.

To take it to the next level, check out this page of reactable demos on Github.

LS0tCnRpdGxlOiAiQ2hvY29sYXRlIG9uIHRoZSB0YWJsZTogRXhwbG9yaW5nIHJlYWN0YWJsZSB0aHJvdWdoIGNob2NvbGF0ZSByYXRpbmdzIgphdXRob3I6ICJKdWxpZXR0ZSBSaWhsIgpkYXRlOiAiNC8zMC8yMDIyIgpvdXRwdXQ6IAogICBodG1sX2RvY3VtZW50OgogICAgICB0aGVtZTogY2VydWxlYW4KICAgICAgY29kZV9kb3dubG9hZDogdHJ1ZQogICAgICBjb2RlX2ZvbGRpbmc6IHNob3cKICAgICAgdG9jOiB0cnVlCiAgICAgIHRvY19mbG9hdDogdHJ1ZQotLS0KCiMgSW50cm9kdWN0aW9uCgpUaGlzIG1hcmtkb3duIHdpbGwgZXhwbGFpbiBob3cgdG8gY3JlYXRlIGEgfnNpbGt5IHNtb290aH4gZGF0YSB0YWJsZSB1c2luZyByZWFjdGFibGUuIFByZXR0eSBzd2VldCwgcmlnaHQ/IChPa2F5LCBlbm91Z2ggY2hvY29sYXRlIHB1bnMuKSAKClRoZSBkYXRhIGlzIHJhbmtpbmdzIG9mIG92ZXIgMiw1MDAgcGxhaW4gZGFyayBjaG9jb2xhdGUgYmFycyBhbmQgaW5jbHVkZXMgY291bnRyeSBvZiBvcmlnaW4sIG1hbnVmYWN0dXJlciwgY29jb2EgcGVyY2VudCBhbmQgbW9yZS4gSXQgd2FzIGNvbXBpbGVkIG9uIGEgd2Vic2l0ZSBjYWxsZWQgW0ZsYXZvcnMgb2YgQ2FjYW9dKGh0dHA6Ly9mbGF2b3Jzb2ZjYWNhby5jb20vY2hvY29sYXRlX2RhdGFiYXNlLmh0bWwpLiBJdCdzIHVuY2xlYXIgd2hvJ3MgYmVoaW5kIHRoZSByYXRpbmdzLCBidXQgd2hvZXZlciBpdCBpcywgdGhleSBsZWZ0IGEgW3ByZXR0eSBpbnZvbHZlZCBndWlkZV0oaHR0cDovL2ZsYXZvcnNvZmNhY2FvLmNvbS9yZXZpZXdfZ3VpZGUuaHRtbCkgdG8gZ2l2ZSB1cyBpbnNpZ2h0IGludG8gdGhlaXIgcHJvY2Vzcy4gCgpJIHN0dW1ibGVkIGFjcm9zcyB0aGUgZGF0YSBvbiB0aGUgW1RpZHkgVHVlc2RheSBHaXRodWIgcGFnZV0oaHR0cHM6Ly9naXRodWIuY29tL3Jmb3JkYXRhc2NpZW5jZS90aWR5dHVlc2RheSksIHdoaWNoIGhhcyBsb3RzIG9mIGdyZWF0IGRhdGFzZXRzIHRvIHByYWN0aWNlIG9uIGluIFIuIAoKTGV0J3MgZ2V0IHN0YXJ0ZWQhCgoKIyBTdGVwIDE6IEdldCBzZXQgdXAKCk1ha2Ugc3VyZSB5b3UgcmVhZCBpbiB5b3VyIGRhdGEgYW5kIHJ1biBsaWJyYXJ5KHJlYWN0YWJsZSksIGFsb25nIHdpdGggd2hhdGV2ZXIgb3RoZXIgbGlicmFyaWVzIHlvdSB3YW50LgoKCmBgYHtyIGVjaG89RkFMU0V9CmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KHJlYWN0YWJsZSkKYGBgCgoKYGBge3IgaW5jbHVkZSwgaW5jbHVkZT1GQUxTRX0KY2hvY29sYXRlX3JhdGluZ3MgPC0gcmVhZF9jc3YoJ2h0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9yZm9yZGF0YXNjaWVuY2UvdGlkeXR1ZXNkYXkvbWFzdGVyL2RhdGEvMjAyMi8yMDIyLTAxLTE4L2Nob2NvbGF0ZS5jc3YnKQpgYGAKCiMgU3RlcCAyOiBDaG9vc2UgeW91ciBjb2x1bW5zCgpUaGVyZSBhcmUgYSBsb3Qgb2YgY29sdW1ucyBpbiB0aGlzIGRhdGFzZXQsIGFuZCBub3QgYWxsIG9mIHRoZW0gYXJlIHJlbGV2YW50IGZvciBteSB0YWJsZS4gU28gSSdtIGdvaW5nIHRvIHNlbGVjdCB3aGljaCBjb2x1bW5zIEkgd2FudCBhbmQgbWFrZSBhIG5ldyBkYXRhZnJhbWUgd2l0aCBvbmx5IHRob3NlIGNvbHVtbnMgaW5jbHVkZWQuCgpgYGB7cn0KdGFibGVfY2hvY29sYXRlX3JhdGluZ3MgPC0KICBjaG9jb2xhdGVfcmF0aW5ncyAlPiUgCiAgc2VsZWN0KGNvbXBhbnlfbWFudWZhY3R1cmVyLCBjb3VudHJ5X29mX2JlYW5fb3JpZ2luLCBjb2NvYV9wZXJjZW50LCBtb3N0X21lbW9yYWJsZV9jaGFyYWN0ZXJpc3RpY3MsIHJhdGluZykgCmBgYAoKIyBTdGVwIDM6IE1ha2UgYSBiYXNpYyByZWFjdGFibGUgCgpUaGlzIGlzIGRvbmUgd2l0aCB0aGUgZnVuY3Rpb24gcmVhY3RhYmxlKHlvdXJfZGF0YV9mcmFtZSkuIAoKYGBge3J9CnJlYWN0YWJsZSh0YWJsZV9jaG9jb2xhdGVfcmF0aW5ncykKYGBgCgpUaGlzIGlzIGFuIG9rYXkgdGFibGUuIEl0J3Mgc29ydGFibGUgYnkgZGVmYXVsdCwgd2hpY2ggaXMgbmljZSDigJQganVzdCBnbyB0byBhbnkgY29sdW1uIGhlYWRlciBhbmQgY2xpY2sgdGhlIHVwIGFuZCBkb3duIGFycm93cyB0byBzb3J0IGJ5IGFzY2VuZGluZy9kZXNjZW5kaW5nLiAoU28gZm9yIGV4YW1wbGUsIHlvdSBjb3VsZCBzb3J0IGl0IGluIG9yZGVyIG9mIHJhdGluZyBmcm9tIGJlc3QgdG8gd29yc2UuKSBCdXQgd2UgY2FuIGRvIGJldHRlciEKCgojIFN0ZXAgNDogQWRkIHNvbWUgc2VtaS1mYW5jeSBmZWF0dXJlcyAKCkhlcmUgYXJlIGEgZmV3IGZlYXR1cmVzIEknbGwgYWRkIGluOgoKKiBzZWFyY2hhYmxlID0gVFJVRSBtYWtlcyBhIHNlYXJjaCBib3ggaW4gdGhlIHRvcCByaWdodAoqIHJlc2l6YWJsZSA9IFRSVUUgbWFrZXMgZWFjaCBjb2x1bW4gcmVzaXphYmxlIGJ5IGRyYWdnaW5nCiogZmlsdGVyYWJsZSA9IFRSVUUgYWRkcyBhIGZpbHRlciBib3ggdG8gdGhlIHRvcCBvZiBlYWNoIGNvbHVtbgoqIGRlZmF1bHRQYWdlU2l6ZSA9IHNldHMgYSBsaW1pdCB0byB0aGUgbnVtYmVyIG9mIHJvd3MgaW4geW91ciB0YWJsZS4KKiBib3JkZXJlZD0gVFJVRSBhZGRzIGJvcmRlcnMgdG8gdGhlIHRhYmxlLgoKYGBge3J9CnJlYWN0YWJsZSh0YWJsZV9jaG9jb2xhdGVfcmF0aW5ncywgc2VhcmNoYWJsZSA9IFRSVUUsIHJlc2l6YWJsZSA9IFRSVUUsIGZpbHRlcmFibGUgPSBUUlVFLCBkZWZhdWx0UGFnZVNpemUgPSA1LCBib3JkZXJlZCA9IFRSVUUpCmBgYAoKVGhpcyB0YWJsZSBpcyBhbHJlYWR5IGxvb2tpbmcgYmV0dGVyISBCdXQsIHRoZSBoZWFkaW5ncyBhcmUgdWdseS4gTGV0J3Mgd29yayBvbiB0aGF0IG5leHQuIAoKIyBTdGVwIDU6IENsZWFuIHVwIHRoZSBoZWFkaW5ncwoKYGBge3J9CnJlYWN0YWJsZSh0YWJsZV9jaG9jb2xhdGVfcmF0aW5ncywgc2VhcmNoYWJsZSA9IFRSVUUsIHJlc2l6YWJsZSA9IFRSVUUsIGZpbHRlcmFibGUgPSBUUlVFLCBkZWZhdWx0UGFnZVNpemUgPSA1LCBib3JkZXJlZCA9IFRSVUUsCiAgICAgICAgICBjb2x1bW5zID0gbGlzdCgKICAgIGNvbXBhbnlfbWFudWZhY3R1cmVyID0gY29sRGVmKCJDb21wYW55IiksCiAgICBjb2NvYV9wZXJjZW50ID0gY29sRGVmKCJDb2NvYSAlIiksCiAgICBjb3VudHJ5X29mX2JlYW5fb3JpZ2luID0gY29sRGVmKCJDb3VudHJ5IG9mIEJlYW4gT3JpZ2luIiksCiAgICBtb3N0X21lbW9yYWJsZV9jaGFyYWN0ZXJpc3RpY3MgPSBjb2xEZWYoIk1vc3QgTWVtb3JhYmxlIENoYXJhY3RlcmlzdGljcyIpLAogICAgcmF0aW5nID0gY29sRGVmKCJSYXRpbmciKSkpCiAgCmBgYAoKQ29vbCEgTGV0J3MgYWRkIGEgZmV3IG1vcmUgZmVhdHVyZXMsIGZvciBmdW4uCgojIFN0ZXAgNjogR3JvdXAgYnkgJ3doYXRldmVyJwoKZ3JvdXBCeSA9ICd2YXJpYWJsZScgYWxsb3dzIHlvdSB0byBncm91cCBieSB3aGF0ZXZlciB2YXJpYWJsZSB5b3UgY2hvb3NlLiBIZXJlLCBJJ2xsIGdyb3VwIGJ5IGNvbXBhbnkgbWFudWZhY3R1cmVyLCBzbyB3ZSBjYW4gc2VlIGFsbCBvZiB0aGUgY2hvY29sYXRlIGJhcnMgbWFkZSBieSBhIGNvbXBhbnkgaW4gb25lIHBsYWNlLiAKCmBgYHtyfQpyZWFjdGFibGUodGFibGVfY2hvY29sYXRlX3JhdGluZ3MsIHNlYXJjaGFibGUgPSBUUlVFLCByZXNpemFibGUgPSBUUlVFLCBmaWx0ZXJhYmxlID0gVFJVRSwgZGVmYXVsdFBhZ2VTaXplID0gNSwgYm9yZGVyZWQgPSBUUlVFLAogICAgICAgICAgY29sdW1ucyA9IGxpc3QoCiAgICBjb21wYW55X21hbnVmYWN0dXJlciA9IGNvbERlZigiQ29tcGFueSIpLAogICAgY29jb2FfcGVyY2VudCA9IGNvbERlZigiQ29jb2EgJSIpLAogICAgY291bnRyeV9vZl9iZWFuX29yaWdpbiA9IGNvbERlZigiQ291bnRyeSBvZiBCZWFuIE9yaWdpbiIpLAogICAgbW9zdF9tZW1vcmFibGVfY2hhcmFjdGVyaXN0aWNzID0gY29sRGVmKCJNb3N0IE1lbW9yYWJsZSBDaGFyYWN0ZXJpc3RpY3MiKSwKICAgIHJhdGluZyA9IGNvbERlZigiUmF0aW5nIikpLAogICAgZ3JvdXBCeSA9ICdjb21wYW55X21hbnVmYWN0dXJlcicpCmBgYAoKU3dlZXQhIEFkZGluZyBhIGZldyBmaW5hbCBmZWF0dXJlcy4uLgoKIyBTdGVwIDc6IEZpbmFsIHRvdWNoZXMKCiogaGlnaGxpZ2h0ID0gVFJVRSBoaWdobGlnaHRzIGEgcm93IHdoZW4geW91IGhvdmVyIG92ZXIgaXQuCiogY29tcGFjdCA9IFRSVUUgbWFrZXMgdGhlIHRhYmxlIG5pY2UgYW5kIGNvbXBhY3QuIAoKYGBge3J9CnJlYWN0YWJsZSh0YWJsZV9jaG9jb2xhdGVfcmF0aW5ncywgc2VhcmNoYWJsZSA9IFRSVUUsIHJlc2l6YWJsZSA9IFRSVUUsIGZpbHRlcmFibGUgPSBUUlVFLCBkZWZhdWx0UGFnZVNpemUgPSA1LCBib3JkZXJlZCA9IFRSVUUsIGhpZ2hsaWdodCA9IFRSVUUsIGNvbXBhY3QgPSBUUlVFLAogICAgICAgICAgY29sdW1ucyA9IGxpc3QoCiAgICBjb21wYW55X21hbnVmYWN0dXJlciA9IGNvbERlZigiQ29tcGFueSIpLAogICAgY29jb2FfcGVyY2VudCA9IGNvbERlZigiQ29jb2EgJSIpLAogICAgY291bnRyeV9vZl9iZWFuX29yaWdpbiA9IGNvbERlZigiQ291bnRyeSBvZiBCZWFuIE9yaWdpbiIpLAogICAgbW9zdF9tZW1vcmFibGVfY2hhcmFjdGVyaXN0aWNzID0gY29sRGVmKCJNb3N0IE1lbW9yYWJsZSBDaGFyYWN0ZXJpc3RpY3MiKSwKICAgIHJhdGluZyA9IGNvbERlZigiUmF0aW5nIikpLAogICAgZ3JvdXBCeSA9ICdjb21wYW55X21hbnVmYWN0dXJlcicpCmBgYApBbmQgd2UncmUgZG9uZSEgVGhlcmUgaXMgYSBsb3QgbW9yZSB5b3UgY2FuIGRvIHdpdGggdGhlc2UgdGFibGVzLCBidXQgdGhpcyBpcyBzb21lIG9mIHRoZSBiYXNpY3MuCgpUbyB0YWtlIGl0IHRvIHRoZSBuZXh0IGxldmVsLCBjaGVjayBvdXQgW3RoaXMgcGFnZSBvZiByZWFjdGFibGUgZGVtb3Mgb24gR2l0aHViXShodHRwczovL2dsaW4uZ2l0aHViLmlvL3JlYWN0YWJsZS9pbmRleC5odG1sKS4K