Search code examples
rshinyreactable

Downloading filtered and sorted data from a reactable in a Shiny app


I'm developing a Shiny app that utilizes the reactable package for tabular data representation. Users can filter and sort columns in the table. I'd like to provide a download button that allows users to download the table data considering the filtering and sorting they may have done.

I am familiar with the rhandsontable package which provides the hot_to_r() function to achieve similar functionality. I'm looking for a parallel solution for reactable.

Below is a basic Shiny app that attempts to provide the desired functionality:

library(shiny)
library(reactable)

ui <- fluidPage(
  downloadButton("downloadData", "Download Data"),
  reactableOutput("my_table")
)

server <- function(input, output, session) {
  
  output$my_table <- renderReactable({
    reactable(head(mtcars), filterable = TRUE)
  })
  
  # Placeholder for downloadHandler
  output$downloadData <- downloadHandler(
    filename = function() {
      paste("data.csv")
    },
    content = function(file) {
      # Attempt to capture and save the current state of the table
      fwrite(as.data.table(head(mtcars)), file)
    },
    contentType = 'text/csv'
  )
}

shinyApp(ui, server)

For instance, I filtered for a speed of 10 and sorted the data by the dist column. How can I download the data displayed in the picture below?

enter image description here


Solution

  • EDIT

    Building on the approach by Victor Perrier we could get the sorted and filtered data via Reactable.getState('my_table').sortedData and pass it to shiny via Shiny.setInputValue. Additionally I had to add an input handler to convert the list into a data.frame:

    library(shiny)
    library(reactable)
    
    convertToDataFrame <- function(x, session, inputname) {
      do.call("rbind.data.frame", x)
    }
    
    registerInputHandler("to_csv", convertToDataFrame, force = TRUE)
    
    ui <- fluidPage(
      downloadButton(
        "downloadData", "Download",
        onClick = "Shiny.setInputValue('table_state:to_csv', Reactable.getState('my_table').sortedData)"
      ),
      reactableOutput("my_table"),
      tableOutput("test")
    )
    
    server <- function(input, output) {
      output$my_table <- renderReactable({
        reactable(head(mtcars), filterable = TRUE)
      })
      output$test <- renderTable({
        input$table_state
      })
      output$downloadData <- downloadHandler(
        filename = function() {
          paste("data-", Sys.Date(), ".csv", sep = "")
        },
        content = function(file) {
          data <- input$table_state
          write.csv(data, file)
        }
      )
    }
    
    shinyApp(ui, server)
    

    enter image description here

    Original Post

    Following the example in the docs you can download your filtered and sorted data table by using the reactable download handler for the onclick= event.

    library(shiny)
    library(reactable)
    
    ui <- fluidPage(
      downloadButton("foo",
        "Download Data",
        onclick = "Reactable.downloadDataCSV('my_table', 'data.csv')"
      ),
      reactableOutput("my_table")
    )
    
    server <- function(input, output, session) {
      output$my_table <- renderReactable({
        reactable(head(mtcars), filterable = TRUE)
      })
    }
    
    shinyApp(ui, server)
    

    This is the result when filtering for cyl=6 and sorting by mpg:

    enter image description here