You may want to customize how your data is displayed beyond what the built-in formatters provide. For example, inserting a link, combining data from multiple columns, or showing a column total that updates on filtering.

In reactable, you can customize data rendering using either an R or JavaScript function that returns custom content:

R render functions JavaScript render functions
reactable(iris, columns = list(
  Species = colDef(
    cell = function(value) {
      htmltools::tags$b(value)
    })
  )
)
reactable(iris, columns = list(
  Species = colDef(
    cell = JS("function(cellInfo) {
      return '<b>' + cellInfo.value + '</b>'
    }")
  )
))
  • Easier to use but more static
  • Render once, when the table is created
  • Supports htmltools and htmlwidgets
  • Harder to use but more dynamic
  • Render on the fly, based on client-side state
  • Can be more efficient in large tables

Whichever one to use depends on the situation and personal preference. You might prefer to use R render functions except in cases where you need more dynamic behavior (e.g., render based on filtered state) or have a very large table.

Example: column total with filtering

For example, you can easily add a column total using an R render function:

data <- MASS::Cars93[20:24, c("Manufacturer", "Model", "Type", "Price")]

reactable(data, searchable = TRUE, columns = list(
  Price = colDef(footer = function(values) {
    htmltools::tags$b(sprintf("$%.2f", sum(values)))
  }),
  Manufacturer = colDef(footer = htmltools::tags$b("Total"))
))

However, the column total doesn’t update with filtering. For that, you need a JavaScript render function with access to the client-side filtered state:

data <- MASS::Cars93[20:24, c("Manufacturer", "Model", "Type", "Price")]

reactable(data, searchable = TRUE, columns = list(
  Price = colDef(
    html = TRUE,
    footer = JS("function(colInfo) {
      var values = colInfo.data.map(function(row) {
        return row[colInfo.column.id]
      })
      var total = values.reduce(function(a, b) { return a + b }, 0)
      return '<b>$' + total.toFixed(2) + '</b>'
    }")
  ),
  Manufacturer = colDef(html = TRUE, footer = "<b>Total</b>")
))

Cells

R render function

To customize cell rendering, provide an R function with up to 3 optional arguments:

colDef(
  cell = function(value, index, name) {
    # input:
    #   - value, the cell value
    #   - index, the row index (optional)
    #   - name, the column name (optional)
    #
    # output:
    #   - content to render (e.g. an HTML tag or widget)
    htmltools::div(style = "color: red", toupper(value))
  }
)

JavaScript render function

Or a JavaScript function, wrapped in JS(), with a single argument:

colDef(
  cell = JS("
    function(cellInfo) {
      // input:
      //  - cellInfo, an object containing cell and row info
      //
      // output:
      //  - content to render (e.g. an HTML string)
      return '<div>' + cellInfo.value + '</div>'
    }
  "),
  html = TRUE  # to render as HTML
)

With JavaScript functions, you can also customize rendering of aggregated cells:

colDef(
  aggregated = JS("function(cellInfo) { return cellInfo.value }")
)

cellInfo properties

Headers

R render function

To customize header rendering, provide an R function with up to 2 optional arguments:

colDef(
  header = function(value, name) {
    # input:
    #   - value, the header value
    #   - name, the column name (optional)
    #
    # output:
    #   - content to render (e.g. an HTML tag or widget)
    htmltools::div(value)
  }
)

JavaScript render function

Or a JavaScript function with a single argument:

colDef(
  header = JS("
    function(colInfo) {
      // input:
      //  - colInfo, an object containing column info
      //
      // output:
      //  - content to render (e.g. an HTML string)
      return '<div>' + colInfo.column.name + '</div>'
    }
  "),
  html = TRUE  # to render as HTML
)

colInfo properties

Footers

R render function

To add footer content, provide an R function with up to 2 optional arguments:

colDef(
  footer = function(values, name) {
    # input:
    #   - values, the column values
    #   - name, the column name (optional)
    #
    # output:
    #   - content to render (e.g. an HTML tag or widget)
    htmltools::div(paste("Total:", sum(values)))
  }
)

JavaScript render function

Or a JavaScript function with a single argument:

colDef(
  footer = JS("
    function(colInfo) {
      // input:
      //  - colInfo, an object containing column info
      //
      // output:
      //  - content to render (e.g. an HTML string)
      return '<div>Rows: ' + colInfo.data.length + '</div>'
    }
  "),
  html = TRUE  # to render as HTML
)

colInfo properties

Expandable Row Details

R render function

To add expandable row details, provide an R function with a single argument:

reactable(
  details = function(index) {
    # input:
    #   - index, the row index
    #
    # output:
    #   - content to render (e.g. an HTML tag or subtable), or NULL to hide details for the row
    htmltools::div(
      paste("Details for row:", index),
      reactable(data[index, ])
    )
  }
)

JavaScript render function

Or a JavaScript function with a single argument:

reactable(
  details = JS("
    function(rowInfo) {
      // input:
      //  - rowInfo, an object containing row info
      //
      // output:
      //  - content to render (e.g. an HTML string)
      return '<div>' + JSON.stringify(rowInfo) + '</div>'
    }
  ")
)

rowInfo properties