options

{handsontable} has 4 main functions to help you configure options for the table:

handsontable

handsontable() allows you to configure table-wide options.

data

  • your table data. must be a data.frame or a matrix object.
handsontable(data = iris)

width

  • sets the width of the table
handsontable(
  data = iris[1:3, ],
  width = 400,
  adaptiveHeight = TRUE
)

height

  • sets the height of the table
handsontable(
  data = iris[1:50, ],
  height = 200
)

adaptiveHeight

  • if TRUE the table’s height adapts to fit it’s contents. useful when you want to show the full table regardless of the number of rows in your data.

  • if TRUE, overrides the height parameter.

  • adaptiveHeight = FALSE:

handsontable(
  data = mtcars,
  rowHeaders = FALSE,
  adaptiveHeight = FALSE
)
  • adaptiveHeight = TRUE:
handsontable(
  data = mtcars,
  rowHeaders = FALSE,
  adaptiveHeight = TRUE
)

colHeaders

  • column names to use.
  • can be a logical (TRUE or FALSE) or a character vector of column names to use.
  • colHeaders = TRUE uses the column names of the data:
handsontable(
  data = iris[1:3, ],
  colHeaders = TRUE,
  adaptiveHeight = TRUE
)
  • colHeaders = FALSE omits the column names of the data and uses excel-like naming for the columns ie. “A”, “B”, “C”, etc.
handsontable(
  data = iris[1:3, ],
  colHeaders = FALSE,
  adaptiveHeight = TRUE
)
  • you can as well use a character vector to rename the rendered columns. this doesn’t change the column names of the underlying data.
handsontable(
  data = iris[1:3, ],
  colHeaders = c(
    "Sepal Length",
    "Sepal Width",
    "Petal Length",
    "Petal Width",
    "Species"
  ),
  adaptiveHeight = TRUE
)

rowHeaders

  • row names to use.
  • can be a logical (TRUE or FALSE) or a character vector of row names to use.
  • rowHeaders = TRUE uses row names of the data:
handsontable(
  data = mtcars[1:3, ],
  rowHeaders = TRUE,
  rowHeaderWidth = 200,
  adaptiveHeight = TRUE
)
  • rowHeaders = FALSE omits the row names column:
handsontable(
  data = mtcars[1:3, ],
  rowHeaders = FALSE,
  adaptiveHeight = TRUE
)
  • use a character vector if you want to customize the name of each row:
handsontable(
  data = mtcars[1:3, ],
  rowHeaders = paste0(LETTERS, letters)[1:3],
  adaptiveHeight = TRUE
)

readOnly

  • by default, a handsontable is editable. ie. readOnly = FALSE.
  • set readOnly = TRUE to make the table uneditable.
handsontable(
  data = iris[1:3, ],
  readOnly = TRUE,
  adaptiveHeight = TRUE
)

colWidths

  • use colWidths to configure the width of all or individual columns.

  • if colWidths is a scalar numeric eg. colWidths = 200, this width is used for all columns.

    handsontable(
      data = iris[1:3, ],
      colWidths = 300,
      adaptiveHeight = TRUE
    )
  • if colWidths is a numeric vector of length greater than 1, eg. colWidths = c(100, 200, 300), then the columns corresponding to those indices are given the said widths.

    handsontable(
      data = iris[1:3, ],
      colWidths = c(100, 200, 300), # other columns use default width
      adaptiveHeight = TRUE
    )
  • colWidths can as well be a JavaScript function based on visual indexes.

    handsontable(
      data = iris[1:3, ],
      colWidths = htmlwidgets::JS(
        "
        function(index) {
          return index * 10 + 30;
        }
        "
      ),
      adaptiveHeight = TRUE
    )

    JavaScript uses zero-indexing. so the actual calculations that happen are:

    • 0 × 10 + 30 = 30
    • 1 × 10 + 30 = 40
    • 2 × 10 + 30 = 50
    • 3 × 10 + 30 = 60
    • 4 × 10 + 30 = 70

fixedColumnsLeft

  • number of columns to freeze on the left.

    handsontable(
      data = iris[1:3, ],
      fixedColumnsLeft = 2,
      colWidths = c(100, 100, 500, 700, 900), # force creation of a wide table
      adaptiveHeight = TRUE
    )

fixedRowsTop

  • number of rows to freeze at the top.

    handsontable(
      data = iris[1:50, ],
      height = 300, # force short table
      fixedRowsTop = 3
    )

fixedRowsBottom

  • number of rows to freeze at the bottom.

    handsontable(
      data = iris[1:100, ],
      fixedRowsTop = 3,
      fixedRowsBottom = 2
    )

manualColumnResize

  • should manual column resizing be enabled?
  • can be:
    • a logical. either TRUE (default) or FALSE.

      handsontable(
        data = iris[1:3, ],
        manualColumnResize = TRUE,
        adaptiveHeight = TRUE
      )
      handsontable(
        data = iris[1:3, ],
        manualColumnResize = FALSE,
        adaptiveHeight = TRUE
      )
    • a numeric vector with initial widths.

      handsontable(
        data = iris[1:3, ],
        # set initial width for 1st & 2nd cols while enabling
        # manual column resize:
        manualColumnResize = c(40, 50),
        adaptiveHeight = TRUE
      )

manualColumnMove

  • should manual column reordering be enabled? when enabled, users can drag columns by their headers and reorder them.
  • can be:
    • a logical. either TRUE or FALSE (default).

      handsontable(
        data = iris[1:3, ],
        manualColumnMove = TRUE,
        adaptiveHeight = TRUE
      )
      handsontable(
        data = iris[1:3, ],
        manualColumnMove = FALSE,
        adaptiveHeight = TRUE
      )
    • integer vector with initial order.

      handsontable(
        data = iris[1:3, ],
        manualColumnMove = c(1, 4),
        adaptiveHeight = TRUE
      )

      this enables manual column moving and reorders the columns such that the first two columns are the ones in position 1 and 4 of your data.

      this uses JavaScript’s zero-based indexing so in R terms 1 and 4 correspond to 2 (“Sepal.Width”) and 5 (“Species”), respectively.

manualRowMove

  • should row reordering be enabled? when enabled, users can drag rows by their headers and reorder them.
  • can be:
    • a logical. either TRUE or FALSE (default).

      handsontable(
        data = iris[1:3, ],
        manualRowMove = TRUE,
        adaptiveHeight = TRUE
      )
      handsontable(
        data = iris[1:3, ],
        manualRowMove = FALSE,
        adaptiveHeight = TRUE
      )
    • integer vector with initial order.

      handsontable(
        data = iris[1:3, ],
        manualRowMove = c(2, 0),
        adaptiveHeight = TRUE
      )

      with c(2, 0), rows at indices 2 and 0 (zero-indexed) come first.

manualRowResize

  • enables/disables manual row resizing.
  • can be:
    • a logical. either TRUE (default) or FALSE.

      handsontable(
        data = iris[1:3, ],
        manualRowResize = TRUE,
        adaptiveHeight = TRUE
      )
      handsontable(
        data = iris[1:3, ],
        manualRowResize = FALSE,
        adaptiveHeight = TRUE
      )
    • numeric vector with initial row heights.

      handsontable(
        data = iris[1:3, ],
        manualRowResize = c(40, 50),
        adaptiveHeight = TRUE
      )

      this sets the initial height of the first 2 rows and enables manual row resize.

stretchH

  • defines how columns behave when the declared table width is different from the calculated sum of all column widths.
  • must be a string and one of:
    • "all": default. stretches all columns to fit width of the table.

      handsontable(
        data = iris[1:3, ],
        stretchH = "all",
        adaptiveHeight = TRUE
      )
    • "last": stretches the last column to fit width of the table.

      handsontable(
        data = iris[1:3, ],
        stretchH = "last",
        adaptiveHeight = TRUE
      )
    • "none": will not stretch any columns.

      handsontable(
        data = iris[1:3, ],
        stretchH = "none",
        adaptiveHeight = TRUE
      )

hooks

you will mostly use these with shiny.

handsontable(
  data = iris[1:3, ],
  adaptiveHeight = TRUE,
  afterRender = htmlwidgets::JS(
    'function() {
      console.log("fired after view-rendering engine updates view");
      console.log("eg. when each row is rendered");
    }'
  ),
  afterInit = htmlwidgets::JS(
    'function() {
      console.log("fired immediately after initialization"); 
    }'
  ),
  afterLoadData = htmlwidgets::JS(
    'function() {
      console.log("fired from afterLoadData"); 
    }'
  )
)

see more hooks.

hot_context_menu

with hot_context_menu(), you can configure options for the table’s context menu ie. the menu you see when you right-click on a handsontable.

contextMenu

  • since hot_context_menu() sets the contextMenu option of handsontable, you can use contextMenu directly on handsontable():

    handsontable(
      data = iris[1:3, ],
      contextMenu = TRUE, # enable context menu
      adaptiveHeight = TRUE
    )
    handsontable(
      data = iris[1:3, ],
      contextMenu = FALSE, # disable context menu
      adaptiveHeight = TRUE
    )

defaults

  • hot_context_menu() provides some sensible defaults:

    handsontable(
      data = iris[1:3, ],
      adaptiveHeight = TRUE
    ) |>
      hot_context_menu()
  • if you only want a subset of those, you can provide the opts parameter of hot_context_menu():

    handsontable(
      data = iris[1:3, ],
      adaptiveHeight = TRUE
    ) |>
      hot_context_menu(opts = c("row_above", "col_right", "copy"))

    by default, valid values for opts are:

    • “row_above”,
    • “row_below”,
    • “col_left”,
    • “col_right”,
    • “———”,
    • “remove_row”,
    • “remove_col”,
    • “clear_column”,
    • “make_read_only”,
    • “undo”,
    • “redo”,
    • “cut”,
    • “copy”,
    • “———”,
    • “export_csv”

custom

  • you can define your own customized context menu options by providing a named list of named lists to the customOpts parameter.

    handsontable(
      data = iris[1:3, ],
      adaptiveHeight = TRUE
    ) |>
      hot_context_menu(
        opts = "row_above",
        customOpts = list(
          custom_alert = list(
            name = "Show alert",
            callback = htmlwidgets::JS(
              "
              function() {
                alert('Custom action!');
              }
              "
            )
          )
        )
      )
  • let’s take it further and define an option to add the class “bg-danger” (bootstrap 5) to selected cell(s):

    custom_opts <- list(
      set_bg_red = list(
        name = "Set BG Color to Red",
        callback = htmlwidgets::JS(
          "
          function(key, selection, clickEvent) {
            const hotInstance = this;
    
            selection.forEach(function(range) {
              for (let row = range.start.row; row <= range.end.row; row++) {
                for (let col = range.start.col; col <= range.end.col; col++) {
                  const currentClassName = hotInstance.getCellMeta(row, col).className;
                  if (!currentClassName) {
                    hotInstance.setCellMeta(row, col, 'className', 'bg-danger');
                    continue;
                  }
    
                  const classes = currentClassName.split(' ');
                  if (!classes.includes('bg-danger')) {
                    classes.push('bg-danger');
                  }
    
                  const updatedClasses = classes.join(' ');
                  hotInstance.setCellMeta(row, col, 'className', updatedClasses);
                }
              }
            });
    
            hotInstance.render();
          }
          "
        )
      )
    )
    handsontable(
      data = iris[1:3, ],
      adaptiveHeight = TRUE
    ) |>
      hot_context_menu(
        opts = "row_above",
        customOpts = custom_opts
      )
  • let’s take it even further and define an option to remove the class “bg-danger” from selected cell(s):

    # we'll just add to the previous definition of `custom_opts`:
    custom_opts$remove_bg_red <- list(
      name = "Clear Red Background",
      callback = htmlwidgets::JS(
        "
        function(key, selection, clickEvent) {
          const hotInstance = this;
    
          selection.forEach(function(range) {
            for (let row = range.start.row; row <= range.end.row; row++) {
              for (let col = range.start.col; col <= range.end.col; col++) {
                const currentClassName = hotInstance.getCellMeta(row, col).className;
                if (!currentClassName) {
                  continue;
                }
    
                const updatedClassName = currentClassName
                  .split(' ')
                  .filter(cls => cls !== 'bg-danger')
                  .join(' ')
                  .trim();
    
                hotInstance.setCellMeta(row, col, 'className', updatedClassName);
              }
            }
          });
    
          hotInstance.render();
        }
        "
      )
    )
    handsontable(
      data = iris[1:3, ],
      adaptiveHeight = TRUE
    ) |>
      hot_context_menu(
        opts = "row_above",
        customOpts = custom_opts
      )

hot_col

use hot_col() to configure options for columns. this can be a single column or multiple of them.

readOnly

  • determines whether column values are editable.

  • logical. either TRUE or FALSE. if NULL (default) it’s ignored.

    handsontable(
      data = iris[1:3, ],
      adaptiveHeight = TRUE
    ) |>
      # make first 2 cols read-only:
      hot_col(
        col = c("Sepal.Length", "Sepal.Width"),
        readOnly = TRUE
      )

hidden

  • determines whether a column is displayed.

  • logical, either TRUE (default) or FALSE.

    handsontable(
      data = iris[1:3, ],
      adaptiveHeight = TRUE
    ) |>
      hot_col(
        # hide Petal.Length & Petal.Width:
        col = c("Petal.Length", "Petal.Width"),
        hidden = TRUE
      )

width

  • sets the width of a column

    handsontable(
      data = iris[1:3, ],
      adaptiveHeight = TRUE
    ) |>
      hot_col(
        col = c("Sepal.Length", "Petal.Length"),
        width = 200
      )

hot_row

use hot_row() to configure options for rows. this can be a single row or multiple of them.

readOnly

  • determines whether row values are editable.

  • logical. either TRUE or FALSE. if NULL (default) it’s ignored.

    handsontable(
      data = iris[1:5, ],
      adaptiveHeight = TRUE
    ) |>
      hot_row(
        # make row 1 & 5 read-only:
        row = c(1, 5),
        readOnly = TRUE
      )